以下模板单单注释了如何使用,算法详解可参考 罗穗骞《后缀数组——处理字符串的有力工具》
算法注释:
#include<cstdio> #include<cstring> #include<iostream> #define FOR(a,b,c) for(int a=(b);a<=(c);a++) using namespace std; const int maxn = int s[maxn]; int sa[maxn],c[maxn],t[maxn],t2[maxn]; void build_sa(int m,int n) { int i,*x=t,*y=t2; //提前一次基数排序 将sa[]求出 //基数排序后满足x递增 当x相同时 按照i递增的顺序 //sa[i]在每次k循环完之后都保留着目前排名第i的是谁 for(i=0;i<m;i++) c[i]=0; for(i=0;i<n;i++) c[x[i]=s[i]]++; for(i=1;i<m;i++) c[i]+=c[i-1]; for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i; for(int k=1;k<=n;k<<=1) { //排序第二关键字 //根据已经算出的sa[]算出y[] //y[i]中保存着谁的第二关键字排名第i (i的第二关键字为i+k) int p=0; for(i=n-k;i<n;i++) y[p++]=i; //第二关键字为0的排在前面 for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k; //根据sa求出y //排序第一关键字 //基数排序后满足x递增 当x相同时 按照y递增的顺序 for(i=0;i<m;i++) c[i]=0; for(i=0;i<n;i++) c[x[y[i]]]++; for(i=0;i<m;i++) c[i]+=c[i-1]; for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i]; //交换xy 并计算下一轮的x //此时y中保存着原来的x 新的x是由排序产生的排名 swap(x,y); p=1; x[sa[0]]=0; for(i=1;i<n;i++) x[sa[i]]=y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k]?p-1:p++; //若二元组的第一关键字与第二关键字相同的则名次相同 if(p>=n) break; //全部有序 即没有两个的名次是相等的 则停止倍增 m=p; //m<-总名次数 } } int rank[maxn],height[maxn]; void getHeight(int n) { //H[i]=height[rank[i]] //H[i]>=H[i-1]-1 int i,j,k=0; for(i=0;i<=n;i++) rank[sa[i]]=i; for(i=0;i<n;i++) { if(k) k--; j=sa[rank[i]-1]; while(s[j+k]==s[i+k]) k++; height[rank[i]]=k; } }
使用注释:
#include<cstdio> #include<cstring> #include<iostream> #define FOR(a,b,c) for(int a=(b);a<=(c);a++) using namespace std; const int maxn = //长度范围 int s[maxn]; //字符串对应Ascii码 使用前可提前构造 int sa[maxn],c[maxn],t[maxn],t2[maxn]; //m代表字符最大值 n代表字符串长 //常常需要在字符串末尾加0以防止RE 令s[n]=0 调用build_sa(m,n+1)即可 void build_sa(int m,int n) { int i,*x=t,*y=t2; for(i=0;i<m;i++) c[i]=0; for(i=0;i<n;i++) c[x[i]=s[i]]++; for(i=1;i<m;i++) c[i]+=c[i-1]; for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i; for(int k=1;k<=n;k<<=1) { int p=0; for(i=n-k;i<n;i++) y[p++]=i; for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k; for(i=0;i<m;i++) c[i]=0; for(i=0;i<n;i++) c[x[y[i]]]++; for(i=0;i<m;i++) c[i]+=c[i-1]; for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i]; swap(x,y); p=1; x[sa[0]]=0; for(i=1;i<n;i++) x[sa[i]]=y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k]?p-1:p++; if(p>=n) break; m=p; } } //n代表字符串长度 //调用getHeight(n)即可 int rank[maxn],height[maxn]; void getHeight(int n) { int i,j,k=0; for(i=0;i<=n;i++) rank[sa[i]]=i; //[0,n] for(i=0;i<n;i++) { //不计算height[n] 即末尾0 if(k) k--; j=sa[rank[i]-1]; while(s[j+k]==s[i+k]) k++; height[rank[i]]=k; } }