后缀数组模板
const int N=2e4+10;
int n,sa[N],rnk[N],height[N];
char s[N];
//sa数组表示字典序为i的后缀是谁,rnk数组表示某个后缀的排名
void buildSA(int m=128){
int cnt[N],rnk1[N],rnk2[N],tmpSA[N]; //cnt[i]用来记录rnk小于等于i的子串已经有多少个了,这样可以直接用cnt[rnk[i]]更新sa
//n=strlen(s)+1;
//s[n]=0; 存疑
fill(cnt,cnt+m,0);
for(int i=0;i<n;++i)
cnt[(int)s[i]]++;
for(int i=1;i<m;i++)
cnt[i]+=cnt[i-1];
for(int i=0;i<n;i++)
rnk[i]=cnt[(int)s[i]]-1;
for(int l=1;l<n;l<<=1){
for(int i=0;i<n;++i){
rnk1[i]=rnk[i];
rnk2[i]=(i+l<n?rnk[i+l]:0);
}
memset(cnt,0,sizeof(cnt));
for(int i=0;i<n;++i)
cnt[rnk2[i]]++;
for(int i=1;i<n;++i)
cnt[i]+=cnt[i-1];
for(int i=n-1;i>=0;--i)
tmpSA[--cnt[rnk2[i]]]=i; //--是为了区分rnk相同的串,后访问的排序靠前一些
memset(cnt,0,sizeof(cnt));
for(int i=0;i<n;++i)
cnt[rnk1[i]]++;
for(int i=1;i<n;++i)
cnt[i]+=cnt[i-1];
for(int i=n-1;i>=0;--i)
sa[--cnt[rnk1[tmpSA[i]]]]=tmpSA[i]; //是按照tmpSA的逆序计算,也就是rnk2较大的对应的cnt值会大一些,排在rnk1相同而rnk2较小的后面
//cnt代表每个桶的大小
bool uniq=true;
rnk[sa[0]]=0; //构建新的rank数组
for(int i=1;i<n;++i){
rnk[sa[i]]=rnk[sa[i-1]];
if(rnk1[sa[i]]==rnk1[sa[i-1]]&&rnk2[sa[i]]==rnk2[sa[i-1]])
uniq=false; //这里不能break,因为还要继续把当前rank数组算完
else
rnk[sa[i]]++;
}
if(uniq)
break;
}
}
//height[i]表示位于sa[i-1]和sa[i]的两个后缀的最长前缀
void getHeight() {
int k = 0;
for (int i = 0; i < n - 1; ++i) {
if (k) --k; //新的长度不会比k-1小,是结论
int j = sa[rnk[i] - 1];
while (s[i + k] == s[j + k]) k++;
height[rnk[i]] = k;
}
}
//满足lcp(l,r)=min{height[i+1],...height[r]},故是RMQ问题
void initST(){ //用ST表进行RMQ
for(int i=0;i<n;i++)
ST[i][0]=height[i]; //实际应该是ST[i][1],但这么做方便后面合并
for(int j=1;(1<<j)<=n;j++){
for(int i=0;i+(1<<j)-1<n;i++)
ST[i][j]=min(ST[i][j-1],ST[i+(1<<(j-1))][j-1]);
}
}
//lcp:最长公共前缀,一般指两个后缀的最长公共前缀
int lcp(int l,int r){
if(l==r)
return n-sa[l];
if(l>r)
swap(l,r);
l++; //因为现在ST表中存的都比下标早一个
int k=0;
while(1<<(k+1)<=r-l+1) ++k;
return min(ST[l][k],ST[r-(1<<k)+1][k]);
}
题目很妙,难点在于想到用后缀数组来解决问题。