UOJ #35. 后缀排序 后缀数组 模板
模板题,重新理了一遍关系。看注释吧。充分理解了倍增的意义,翻倍之后对上一次排序的利用是通过一种类似于队列的方式完成的。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 const int maxn=200010; 8 const int pl=50; 9 int sa[maxn+pl]={};//排名第i的是从sa[i]开始的数组 10 int rk[maxn+pl]={};//i的排名 11 int height[maxn+pl]={};//排名第i的与排名第i-1的最长相同前缀长度 12 int temp[maxn+pl]={};//暂时的排名 13 int cnt[maxn+pl]={};//第i种(字典序)前缀的有多少个(的前缀和) 14 int p[maxn+pl]={};//此次需要排列的sa的储存,处理了后缀长度不同的情况。 15 char ch[maxn+pl]={}; 16 int siz; 17 bool equ(int x,int y,int l){ return rk[x]==rk[y]&&rk[x+l]==rk[y+l]; } 18 void SA(){ 19 for(int i=1;i<=siz;i++){rk[i]=ch[i];sa[i]=i;} 20 for(int i,sig=255,l=0,pos=0;pos<siz;sig=pos){ 21 pos=0; 22 for(i=siz-l+1;i<=siz;i++)p[++pos]=i;//根据上一次的sa决定p的储存顺序,没有排序则排在最前。 23 for(i=1;i<=siz;i++) if(sa[i]>l) p[++pos]=sa[i]-l;//sa的计算是向字符串前端(左侧)拓展的 24 for(i=1;i<=sig;i++) cnt[i]=0; 25 for(i=1;i<=siz;i++) cnt[rk[p[i]]]++; 26 for(i=1;i<=sig;i++) cnt[i]+=cnt[i-1]; 27 for(i=siz;i>0;i--) sa[cnt[rk[p[i]]]--]=p[i];//方便记忆板子的提示,只有这里是倒着扫的。 28 pos=0; 29 for(i=1;i<=siz;i++){ 30 if(equ(sa[i],sa[i-1],l))temp[sa[i]]=pos; 31 else temp[sa[i]]=++pos; 32 } 33 for(int i=1;i<=siz;i++)rk[i]=temp[i]; 34 if(!l)l=1; 35 else l<<=1; 36 }int j=0; 37 for(int i=1;i<=siz;i++){ 38 if(rk[i]==1){j=0;continue;} 39 if(j)j--; 40 while(ch[i+j]==ch[sa[rk[i]-1]+j]){ 41 j++; 42 }height[rk[i]]=j; 43 } 44 } 45 int main(){ 46 //freopen("a.in","r",stdin); 47 scanf("%s",ch+1);siz=strlen(ch+1); 48 SA(); 49 for(int i=1;i<=siz;i++)printf("%d ",sa[i]); 50 cout<<endl; 51 for(int i=2;i<=siz;i++)printf("%d ",height[i]); 52 return 0; 53 }