suffix(i):从第i个字符开始的后缀
sa[i]:后缀数组,“排第几的是谁”,即suffix(sa[i])<suffix(sa[i+1]),1<=i<n。
ra[i]:名次数组,“你排第几”,ra[i]表示的是suffix(i)在所有后缀中从小到大排列的“名次”
height[i]:suffix(sa[i-1])和suffix(sa[i])的最长公共前缀,也就是排名相邻的两个后缀的最长公共前缀。
对于j和k,设rank[j]<rank[j],则有:
suffix(j)和suffix(k)的最长公共前缀为height[ra[j]+1],height[ra[j]+2],height[ra[j]+3],...,height[rank[k]]中的最小值。
后缀数组的应用有:
1、最长公共前缀
2.1、重复子串:可重叠最长重复子串、不可重叠最长重复子串、可重叠k次最长重复子串
2.2、子串的个数
1、洛谷 P3809 【模板】后缀排序 (最长公共前缀)
参考这里,以后求后缀数组都用倍增法吧。
输入的字符串以0开始计数,长度为n,在字符串末尾加个比输入字符都小的字符使其长度变为n+1。
求得的height数组可以把数组当成以1开始计数的。

1 #include<iostream> 2 #include<sstream> 3 #include<fstream> 4 #include<algorithm> 5 #include<cstring> 6 #include<iomanip> 7 #include<cstdlib> 8 #include<cctype> 9 #include<vector> 10 #include<string> 11 #include<cmath> 12 #include<ctime> 13 #include<stack> 14 #include<queue> 15 #include<map> 16 #include<set> 17 #define mem(a,b) memset(a,b,sizeof(a)) 18 #define random(a,b) (rand()%(b-a+1)+a) 19 #define ll long long 20 #define ull unsigned long long 21 #define e 2.71828182 22 #define Pi acos(-1.0) 23 #define ls(rt) (rt<<1) 24 #define rs(rt) (rt<<1|1) 25 #define lowbit(x) (x&(-x)) 26 using namespace std; 27 const int MAXN=1e6+5; 28 char str[MAXN]; 29 int n,m; 30 int sa[MAXN],ra[MAXN],height[MAXN]; 31 int wa[MAXN],wb[MAXN],wc[MAXN],wd[MAXN]; 32 int read() 33 { 34 int s=1,x=0; 35 char ch=getchar(); 36 while(!isdigit(ch)) {if(ch=='-') s=-1;ch=getchar();} 37 while(isdigit(ch)) {x=10*x+ch-'0';ch=getchar();} 38 return x*s; 39 } 40 void GetSaRa(int n,int m)//倍增法 nlogn 41 { 42 int i,j,p,*x=wa,*y=wb,*t; 43 for(int i=0;i<m;++i) wd[i]=0; 44 for(int i=0;i<n;++i) wd[x[i]=str[i]]++; 45 for(int i=1;i<m;++i) wd[i]+=wd[i-1]; 46 for(int i=n-1;i>=0;--i) sa[--wd[x[i]]]=i; 47 for(j=1,p=1;p<n;j*=2,m=p) 48 { 49 for(p=0,i=n-j;i<n;++i) y[p++]=i; 50 for(i=0;i<n;++i) if(sa[i]>=j) y[p++]=sa[i]-j; 51 for(i=0;i<n;++i) wc[i]=x[y[i]]; 52 for(i=0;i<m;++i) wd[i]=0; 53 for(i=0;i<n;++i) wd[wc[i]]++; 54 for(i=1;i<m;++i) wd[i]+=wd[i-1]; 55 for(i=n-1;i>=0;--i) sa[--wd[wc[i]]]=y[i]; 56 for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;++i) 57 x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++; 58 } 59 } 60 void GetHeight(int n) 61 { 62 int i,j,k=0; 63 for(i=1;i<=n;++i) ra[sa[i]]=i; 64 for(i=0;i<n;height[ra[i++]]=k) 65 for(k?k--:0,j=sa[ra[i]-1];str[i+k]==str[j+k];++k); 66 } 67 int main() 68 { 69 scanf("%s",str); 70 n=strlen(str); 71 str[n]='0';m=200; 72 GetSaRa(n+1,m); 73 //GetHeight(n); 74 //for(int i=0;i<=n;++i) cout<<sa[i]<<' ';cout<<endl; //含末尾加的字符 75 for(int i=1;i<=n;++i) cout<<sa[i]+1<<' ';cout<<endl;//不含末尾加的字符 76 //for(int i=0;i<n;++i) cout<<ra[i]<<' ';cout<<endl; 77 //for(int i=2;i<=n;++i)cout<<height[i]<<' ';cout<<endl; 78 }
后缀数组求单个字符串中不同子串任意两个的最长公共前缀。利用height数组和ST表。
详见代码。

1 #include<iostream> 2 #include<sstream> 3 #include<fstream> 4 #include<algorithm> 5 #include<cstring> 6 #include<iomanip> 7 #include<cstdlib> 8 #include<cctype> 9 #include<vector> 10 #include<string> 11 #include<cmath> 12 #include<ctime> 13 #include<stack> 14 #include<queue> 15 #include<map> 16 #include<set> 17 #define mem(a,b) memset(a,b,sizeof(a)) 18 #define random(a,b) (rand()%(b-a+1)+a) 19 #define ll long long 20 #define ull unsigned long long 21 #define e 2.71828182 22 #define Pi acos(-1.0) 23 #define ls(rt) (rt<<1) 24 #define rs(rt) (rt<<1|1) 25 #define lowbit(x) (x&(-x)) 26 using namespace std; 27 const int MAXN=1e5+5; 28 int n,m; 29 char str[MAXN]; 30 int sa[MAXN],ra[MAXN],height[MAXN]; 31 int wa[MAXN],wb[MAXN],wc[MAXN],wd[MAXN]; 32 int read() 33 { 34 int s=1,x=0; 35 char ch=getchar(); 36 while(!isdigit(ch)) {if(ch=='-') s=-1;ch=getchar();} 37 while(isdigit(ch)) {x=10*x+ch-'0';ch=getchar();} 38 return x*s; 39 } 40 void GetSaRa(int n,int m)//倍增法 nlogn 41 { 42 int i,j,p,*x=wa,*y=wb,*t; 43 for(int i=0;i<m;++i) wd[i]=0; 44 for(int i=0;i<n;++i) wd[x[i]=str[i]]++; 45 for(int i=1;i<m;++i) wd[i]+=wd[i-1]; 46 for(int i=n-1;i>=0;--i) sa[--wd[x[i]]]=i; 47 for(j=1,p=1;p<n;j*=2,m=p) 48 { 49 for(p=0,i=n-j;i<n;++i) y[p++]=i; 50 for(i=0;i<n;++i) if(sa[i]>=j) y[p++]=sa[i]-j; 51 for(i=0;i<n;++i) wc[i]=x[y[i]]; 52 for(i=0;i<m;++i) wd[i]=0; 53 for(i=0;i<n;++i) wd[wc[i]]++; 54 for(i=1;i<m;++i) wd[i]+=wd[i-1]; 55 for(i=n-1;i>=0;--i) sa[--wd[wc[i]]]=y[i]; 56 for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;++i) 57 x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++; 58 } 59 } 60 int dp[MAXN][20]; 61 void ST(int n) 62 { 63 for(int i=1;i<=n;++i) dp[i][0]=height[i]; 64 for(int j=1;(1<<j)<=n;++j) 65 for(int i=1;i+(1<<j)-1<=n;++i) 66 dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]); 67 } 68 int RMQ(int l,int r) 69 { 70 int k=0; 71 while((1<<(k+1))<=r-l+1) ++k; 72 return min(dp[l][k],dp[r-(1<<k)+1][k]); 73 } 74 void GetHeight(int n) 75 { 76 int i,j,k=0; 77 for(i=1;i<=n;++i) ra[sa[i]]=i; 78 for(i=0;i<n;height[ra[i++]]=k) 79 for(k?k--:0,j=sa[ra[i]-1];str[i+k]==str[j+k];++k); 80 } 81 int main() 82 { 83 int t,l1,r1,l2,r2; 84 while(~scanf("%s",str)) 85 { 86 n=strlen(str);str[n]='0';m=233; 87 GetSaRa(n+1,m);GetHeight(n); 88 ST(n); 89 scanf("%d",&t); 90 ll input=t,output=2*t+1;//先把换行、空格加上 91 scanf("%d%d",&l1,&r1); 92 input+=(r1-l1); 93 output+=(r1-l1);//第一个肯定不能压缩 94 t--; 95 while(t--) 96 { 97 scanf("%d%d",&l2,&r2); 98 input+=r2-l2; 99 int tmp; 100 if(l2==l1) tmp=min(r1-l1,r2-l2); 101 else 102 { 103 int k1=ra[l1],k2=ra[l2]; 104 if(k1>k2) swap(k1,k2); 105 tmp=RMQ(k1+1,k2); 106 } 107 //因为这儿的后缀不是完整的,取短的,这里的tmp是两个子串的最长公共前缀长度 108 tmp=min(tmp,min(r1-l1,r2-l2)); 109 if(!tmp) output++; 110 else output+=(int)(log10(tmp)+1);//位数,题目规定一个数字一个字节 111 output+=(r2-l2-tmp);//剩下的不能复制的 112 l1=l2,r1=r2; 113 } 114 cout<<input<<' '<<output<<endl; 115 } 116 }
后缀数组求不可重叠最长重复子串。二分+sa数组+height数组。
这题有个坑,求出差分数组后,用来二分的判断函数里,详细见代码。
关于后缀数组中height数组的分组,这个讲的蛮好的:http://www.mamicode.com/info-detail-1057844.html
i从1到N遍历,通过height[i]>=k将i分开,即将后缀分成若干组,每组中的后缀的公共前缀长度均大于等于k,且可以肯定组A中的某后缀t1和组B中的某后缀t2的公共前缀长度小于M
然后就是二分牛批好吧。

1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAXN=2e4+5; 4 int str[MAXN]; 5 int sa[MAXN],ra[MAXN],height[MAXN]; 6 int wa[MAXN],wb[MAXN],wc[MAXN],wd[MAXN]; 7 int read() 8 { 9 int s=1,x=0; 10 char ch=getchar(); 11 while(!isdigit(ch)) {if(ch=='-') s=-1;ch=getchar();} 12 while(isdigit(ch)) {x=10*x+ch-'0';ch=getchar();} 13 return x*s; 14 } 15 void GetSaRa(int n,int m)//倍增法 nlogn 16 { 17 int i,j,p,*x=wa,*y=wb,*t; 18 for(int i=0;i<m;++i) wd[i]=0; 19 for(int i=0;i<n;++i) wd[x[i]=str[i]]++; 20 for(int i=1;i<m;++i) wd[i]+=wd[i-1]; 21 for(int i=n-1;i>=0;--i) sa[--wd[x[i]]]=i; 22 for(j=1,p=1;p<n;j*=2,m=p) 23 { 24 for(p=0,i=n-j;i<n;++i) y[p++]=i; 25 for(i=0;i<n;++i) if(sa[i]>=j) y[p++]=sa[i]-j; 26 for(i=0;i<n;++i) wc[i]=x[y[i]]; 27 for(i=0;i<m;++i) wd[i]=0; 28 for(i=0;i<n;++i) wd[wc[i]]++; 29 for(i=1;i<m;++i) wd[i]+=wd[i-1]; 30 for(i=n-1;i>=0;--i) sa[--wd[wc[i]]]=y[i]; 31 for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;++i) 32 x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++; 33 } 34 } 35 void GetHeight(int n) 36 { 37 int i,j,k=0; 38 for(i=1;i<=n;++i) ra[sa[i]]=i; 39 for(i=0;i<n;height[ra[i++]]=k) 40 for(k?k--:0,j=sa[ra[i]-1];str[i+k]==str[j+k];++k); 41 } 42 bool C(int n,int k)//差分数组中是否存在两个长度为k的子串是相同的 43 { 44 int maxi=-1,mini=1<<30;//下标 45 for(int i=1;i<=n;++i) 46 { 47 if(height[i]<k)//分组 48 maxi=sa[i],mini=sa[i]; 49 else//组内是否有相同长度超过k的子串且距离大于k 50 { 51 maxi=max(maxi,max(sa[i],sa[i-1])); 52 mini=min(mini,min(sa[i],sa[i-1])); 53 //这里为什么不取等于,明明等于也是可以满足的 54 //其实这里求得的满足条件的是差分数组满足而不是原数组满足 55 //最后的结果还要加1就是这么来的 56 if(maxi-mini>k) return true; 57 } 58 } 59 return false; 60 } 61 int main() 62 { 63 //freopen("1.txt","r",stdin); 64 int n,x,tmp; 65 while(scanf("%d",&n),n) 66 { 67 x=read();tmp=x; 68 for(int i=1;i<n;++i) 69 { 70 x=read(); 71 str[i-1]=x-tmp+100;//差分,避免桶排序中出现负的下标多加个100 72 tmp=x; 73 } 74 str[n-1]=0;n--; 75 GetSaRa(n+1,300); 76 GetHeight(n); 77 // for(int i=0;i<n;++i) cout<<str[i]<<' ';cout<<endl; 78 // for(int i=1;i<=n;++i) cout<<height[i]<<' ';cout<<endl; 79 if(!C(n,4)) 80 { 81 cout<<0<<endl; 82 continue; 83 } 84 int l=0,r=n,mid; 85 while(l<r) 86 { 87 mid=(l+r+1)>>1; 88 if(C(n,mid)) l=mid; 89 else r=mid-1; 90 } 91 cout<<l+1<<endl; 92 } 93 return 0; 94 }
懂了一点二分后改成:(以后二分全搞成开区间哈哈哈贼爽)
主要是这个解的上下界挺麻烦确定的,就搞成开区间吧,数值随便,保证开区间就行,输出l还是r还是别的什么找一组输出试试就行了(一般是l,这里加1是因为题意)。
和上题差不多,二分答案,将后缀分成若干组。这里要判断的是有没有一个组的后缀个数不小于k。上题相当于k=2,并且上题不可重叠,这题可重叠。

1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 const int MAXN=2e4+5; 5 const int MAXM=1e6+5; 6 int n,k; 7 int str[MAXN]; 8 int sa[MAXN],ra[MAXN],height[MAXN]; 9 int wa[MAXN],wb[MAXN],wc[MAXN],wd[MAXN]; 10 int read() 11 { 12 int s=1,x=0; 13 char ch=getchar(); 14 while(!isdigit(ch)) {if(ch=='-') s=-1;ch=getchar();} 15 while(isdigit(ch)) {x=10*x+ch-'0';ch=getchar();} 16 return x*s; 17 } 18 void GetSaRa(int n,int m)//倍增法 nlogn 19 { 20 int i,j,p,*x=wa,*y=wb,*t; 21 for(int i=0;i<m;++i) wd[i]=0; 22 for(int i=0;i<n;++i) wd[x[i]=str[i]]++; 23 for(int i=1;i<m;++i) wd[i]+=wd[i-1]; 24 for(int i=n-1;i>=0;--i) sa[--wd[x[i]]]=i; 25 for(j=1,p=1;p<n;j*=2,m=p) 26 { 27 for(p=0,i=n-j;i<n;++i) y[p++]=i; 28 for(i=0;i<n;++i) if(sa[i]>=j) y[p++]=sa[i]-j; 29 for(i=0;i<n;++i) wc[i]=x[y[i]]; 30 for(i=0;i<m;++i) wd[i]=0; 31 for(i=0;i<n;++i) wd[wc[i]]++; 32 for(i=1;i<m;++i) wd[i]+=wd[i-1]; 33 for(i=n-1;i>=0;--i) sa[--wd[wc[i]]]=y[i]; 34 for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;++i) 35 x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++; 36 } 37 } 38 void GetHeight(int n) 39 { 40 int i,j,k=0; 41 for(i=1;i<=n;++i) ra[sa[i]]=i; 42 for(i=0;i<n;height[ra[i++]]=k) 43 for(k?k--:0,j=sa[ra[i]-1];str[i+k]==str[j+k];++k); 44 } 45 bool C(int len)//可重叠k次的最长重复子串长度为len时是否存在 46 { 47 int num=1;//一组中公共前缀长度大于等于len的字串有多少个 48 //height[1]=0 49 for(int i=2;i<=n;++i) 50 { 51 if(height[i]>=len) 52 { 53 num++; 54 if(num>=k) return true; 55 } 56 else num=1; 57 } 58 return false; 59 } 60 int main() 61 { 62 n=read(),k=read(); 63 int maxm=-1; 64 for(int i=0;i<n;++i) str[i]=read(),maxm=max(maxm,str[i]); 65 str[n]=0; 66 GetSaRa(n+1,maxm+5); 67 GetHeight(n); 68 int l=-1,r=n+1; 69 while(l+1!=r) 70 { 71 int mid=(l+r)>>1; 72 if(C(mid)) l=mid; 73 else r=mid; 74 } 75 cout<<l<<endl; 76 }
5、SPOJ Distinct Substrings (VJ)
每个子串一定是某个后缀的前缀,那么原问题等价于求所有后缀之间的不相同的前缀的个数。将所有的后缀按照suffix(sa[1]),suffix(sa[2])、suffix(sa[3]即排名从小到大的顺序计算,对于每一次新加进来的后缀suffix(sa[k]),将产生n-sa[k]个新的前缀(对于长度为k的后缀来说,能产生k个长度不一的子串,n-s[k]求的即是该后缀的长度,此处n指的是原字符串长度),但是其中有height[k]个前缀是和suffix(sa[k-1])重复的,故还要减去height[k]。总的答案加起来即可。

1 //CSDN博客:https://blog.csdn.net/qq_40889820 2 #include<bits/stdc++.h> 3 using namespace std; 4 const int MAXN=1e3+5; 5 char str[MAXN]; 6 int sa[MAXN],ra[MAXN],height[MAXN]; 7 int wa[MAXN],wb[MAXN],wc[MAXN],wd[MAXN]; 8 int read() 9 { 10 int s=1,x=0; 11 char ch=getchar(); 12 while(!isdigit(ch)) {if(ch=='-') s=-1;ch=getchar();} 13 while(isdigit(ch)) {x=10*x+ch-'0';ch=getchar();} 14 return x*s; 15 } 16 void GetSaRa(int n,int m)//倍增法 nlogn 17 { 18 int i,j,p,*x=wa,*y=wb,*t; 19 for(int i=0;i<m;++i) wd[i]=0; 20 for(int i=0;i<n;++i) wd[x[i]=str[i]]++; 21 for(int i=1;i<m;++i) wd[i]+=wd[i-1]; 22 for(int i=n-1;i>=0;--i) sa[--wd[x[i]]]=i; 23 for(j=1,p=1;p<n;j*=2,m=p) 24 { 25 for(p=0,i=n-j;i<n;++i) y[p++]=i; 26 for(i=0;i<n;++i) if(sa[i]>=j) y[p++]=sa[i]-j; 27 for(i=0;i<n;++i) wc[i]=x[y[i]]; 28 for(i=0;i<m;++i) wd[i]=0; 29 for(i=0;i<n;++i) wd[wc[i]]++; 30 for(i=1;i<m;++i) wd[i]+=wd[i-1]; 31 for(i=n-1;i>=0;--i) sa[--wd[wc[i]]]=y[i]; 32 for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;++i) 33 x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++; 34 } 35 } 36 void GetHeight(int n) 37 { 38 int i,j,k=0; 39 for(i=1;i<=n;++i) ra[sa[i]]=i; 40 for(i=0;i<n;height[ra[i++]]=k) 41 for(k?k--:0,j=sa[ra[i]-1];str[i+k]==str[j+k];++k); 42 } 43 int main() 44 { 45 int T=read(); 46 while(T--) 47 { 48 scanf("%s",str); 49 int n=strlen(str); 50 str[n]=0; 51 GetSaRa(n+1,200); 52 GetHeight(n); 53 int ans=0; 54 for(int i=1;i<=n;++i) 55 ans+=n-sa[i]-height[i]; 56 cout<<ans<<endl; 57 } 58 }
哈希做下:(用set判重没过超时了,改成哈希表过了)

1 #include<bits/stdc++.h> 2 #define ull unsigned long long 3 using namespace std; 4 const int MAXN=1e3+5; 5 const int Base=23333; 6 const int mod=14937; 7 ull Hash[MAXN],power[MAXN]; 8 char str[MAXN]; 9 vector<ull> HashTable[mod]; 10 void add_edge(ull h) 11 { 12 int pos=h%mod; 13 HashTable[pos].push_back(h); 14 } 15 int find(ull h) 16 { 17 int pos=h%mod; 18 for(int i=0;i<HashTable[pos].size();++i) 19 if(HashTable[pos][i]==h) return 1; 20 return 0; 21 } 22 int main() 23 { 24 int T; 25 scanf("%d",&T);power[0]=1; 26 for(int i=1;i<=MAXN;++i) power[i]=power[i-1]*Base; 27 while(T--) 28 { 29 scanf("%s",str+1); 30 Hash[0]=0; 31 for(int i=0;i<mod;++i) HashTable[i].clear(); 32 int len=strlen(str+1); 33 int ans=0; 34 for(int i=1;i<=len;++i) Hash[i]=Hash[i-1]*Base+str[i]; 35 for(int i=1;i<=len;++i)//length 36 { 37 for(int j=1;j<=len-i+1;++j)//left 38 { //[j,j+i-1] 39 ull tmp=Hash[i+j-1]-Hash[j-1]*power[i]; 40 if(!find(tmp)) 41 { 42 ans++;add_edge(tmp); 43 } 44 } 45 } 46 cout<<ans<<endl; 47 } 48 }