UOJ #35. 后缀排序 后缀数组 模板

http://uoj.ac/problem/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 }
View Code

 

posted @ 2018-03-05 21:18  鲸头鹳  阅读(145)  评论(0编辑  收藏  举报