后缀数组
从网上找到了一个好的后缀数组模板,特发此随笔以保存,正好最近也在后缀数组。http://blog.csdn.net/u013480600/article/details/44763865
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=20000+1000; struct SuffixArray { //保存原始字符串+‘\0’后形成的字符串 //即原始字符串在s中表示[0,n-2]范围, //然后s[n-1]其实是人为添加的'\0'字符 char s[maxn]; //排名(后缀)数组,sa[i]==j表示字典序为i的后缀是后缀j //其中i从0到n-1,j从0到n-1范围 int sa[maxn]; //名次数组,rank[i]==j表示后缀i的字典序排名为j int rank[maxn]; int height[maxn]; //辅助数组用于x和y数组 int t1[maxn],t2[maxn]; //c[i]==j表示关键字<=i的关键字有j个 int c[maxn]; //s原始字符串+‘\0'字符后的长度 //由于添加了尾0,所以n一般都>=2的 int n;//n>=2,不能等于1,否则build_height()函数可能会出BUG int dmin[maxn][20]; //m大于s[]数组出现的任意字符的int值 void build_sa(int m) { int i,*x=t1,*y=t2; //预处理每个后缀的长度为1的前缀,求出x数组和sa数组 //此时x[i]==j表示第i个字符的绝对值(可以看成是名次数组) //但有可能x[1]=2,且x[3]=2,说明1字符和3字符完全一样。 //此时算出的sa[i]==j表示当前长度为1的字符串的排名数组, //排名数组值不会一样 //就算x[1]==x[3]==2,但是sa[1]=1,而sa[2]=3。 //即就算1号字符和3号字符是完全一样的, //但是排名时第1名是1号字符,第2名才是3号字符 for(i=0;i<m;i++) c[i]=0; for(i=0;i<n;i++) c[x[i]=s[i]]++; //此时c[i]表示关键字<=i的关键字一共有c[i]个 for(i=1;i<m;i++) c[i]+=c[i-1]; //计算当前长度(1)的排名数组 for(i=n-1;i>=0;i--) sa[--c[x[i]]] = i; //每轮循环开始前我们通过之前的计算得到了x[]和sa[]: //每个后缀的长为k的前缀(即每个后缀的前[0,k-1]字符)的名次数组x[], //我们还知道每个后缀的长为k的前缀的排名数组sa[], //然后通过sa[]数组我们可以求得每个后缀的第[k,2*k-1]字符的排名数组y[], //然后通过k字符的x[]与k字符的y[], //我们可以求得每个后缀的长为2k的前缀字符串的sa[]排名数组 //然后通过该sa[]排名数组,和k字符的x数组,我们可以求得2k字符的x[]数组 //以上每轮的x[]名次数组都是可能有重复值大小的,但是sa[]值不会重复 //比如表示k个字符的x[1]=2,x[4]=2时, //那么表示[1,k+1]字符串与[4,k+4]字符完全相同,且排名为2(最高排名为0) //当哪轮求出的x[]数组正好由n个值(即所有值都不重复时) //说明所有后缀已经排序完毕 for(int k=1;k<=n;k<<=1) { //先计算每个后缀的前缀的[k,2*k-1]字符的排名数组y //即y是每个后缀的长为2k前缀的第二关键字 int p=0; //y[p]==i表第二关键字为第p名的是后缀i //由于当前处理的是每个后缀的前缀的[k,2*k-1]字符 //而后缀n-k到后缀n-1不存在第k个字符(想想是不是) //所以他们的第二关键字的名字自然优先 for(i=n-k;i<n;i++) y[p++]=i; //除了上面那些后缀不存在第二关键字 //x+k后缀的第1关键字排名-k 等于 x后缀的第2关键字排名 for(i=0;i<n;i++)if(sa[i]>=k) y[p++]=sa[i]-k; //上面已经计算出了y[],(x[]数组上一轮已经算出) //下面通过第1关键字x[]名次数组和第2关键字y[]排名数组 //计算综合后每个后缀的长2k前缀的sa[]数组 for(i=0;i<m;i++) c[i]=0; for(i=0;i<n;i++) c[x[y[i]]]++; for(i=1;i<m;i++) c[i]+=c[i-1]; for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]] = y[i]; //交换x和y,令y表示名次数组 //计算综合后每个后缀的长2k前缀的x[]数组 swap(x,y); //此时p用来记录x[]数组中不同值的个数 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++; //上面y[sa[i]+k]的sa[i]+k<=n-1,因为只要然后两个不同的后缀必然要分出大小 //所以在他们y[sa[i]]==y[sa[i-1]],即这两个后缀的长k的第一关键字相同的情况下 //他们必定还存在第二关键需要比较 if(p>=n) break; m=p; } } //此函数详解见刘汝佳<<训练指南>>P222 //height[i]表示sa[i-1]后缀与sa[i]后缀的最大公共前缀长度 //即表示排名i-1和排名i的后缀的最大公共前缀LCP长度 //所以height数组只有[1,n-1]是有效下标 void build_height()//n不能等于1,否则出BUG { 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[i+k]==s[j+k]) k++; height[rank[i]]=k; } } void initMin() { for(int i=1;i<=n;i++) dmin[i][0]=height[i]; for(int j=1;(1<<j)<=n;j++) for(int i=1;i+(1<<j)-1<=n;i++) dmin[i][j]=min(dmin[i][j-1] , dmin[i+(1<<(j-1))][j-1]); } int RMQ(int L,int R)//取得范围最小值 { int k=0; while((1<<(k+1))<=R-L+1)k++; return min(dmin[L][k] , dmin[R-(1<<k)+1][k]); } int LCP(int i,int j)//求后缀i和j的LCP最长公共前缀 { int L=rank[i],R=rank[j]; if(L>R) swap(L,R); L++;//注意这里 return RMQ(L,R); } }sa;