SA后缀数组

需要再多去理解

  1 #include<cstdio> 
  2 #include<cstring>
  3 #include<cmath>
  4 #include<ctime>
  5 #include<iostream>
  6 #include<algorithm>
  7 #include<queue>
  8 #include<set>
  9 #define inf (0x7fffffff)
 10 #define l(a) ((a)<<1)
 11 #define r(a) ((a)<<1|1)
 12 #define b(a) (1<<(a))
 13 #define rep(i,a,b) ;for(int i=a;i<=(b);i++)
 14 #define clr(a) memset(a,0,sizeof(a))
 15 typedef long long ll;
 16 typedef unsigned long long ull;
 17 using namespace std;
 18 int readint(){
 19     int t=0,f=1;char c=getchar();
 20     while(!isdigit(c)){
 21         if(c=='-') f=-1;
 22         c=getchar();
 23     }
 24     while(isdigit(c)){
 25         t=(t<<3)+(t<<1)+c-'0';
 26         c=getchar();
 27     }
 28     return t*f;
 29 }
 30 const int maxn=2000009;
 31 int n,m=256,mn=maxn,sa1[maxn],a[maxn],sum[maxn],sa[maxn],rank[maxn],height[maxn];//sa与rank意义相反 
 32 char s[maxn];
 33 void cal_SA(){
 34     clr(sum);
 35     rep(i,1,n) sum[rank[i]=s[i]]++;//求初始rank
 36     rep(i,1,m) sum[i]+=sum[i-1];
 37     for(int i=n;i;i--) sa[sum[rank[i]]--]=i;///基数排序求初始sa 注意这个奇妙的操作! 
 38     for(int l=1;l<n;l<<=1){
 39         //printf("rank==");rep(i,1,n) printf("%d ",rank[i]);puts("");
 40         //printf("sa==");rep(i,1,n) printf("%d ",sa[i]);puts("");
 41         
 42         int cnt=0;
 43         rep(i,n-l+1,n) sa1[++cnt]=i;//后面的第二关键字为最小 
 44         rep(i,1,n) if(sa[i]>l) sa1[++cnt]=sa[i]-l;//利用sa对第二关键字的排序(sa1即第二关键字的sa) 
 45         
 46         //printf("sa1==");rep(i,1,n) printf("%d ",sa1[i]);puts("");
 47         
 48         rep(i,1,n) a[i]=rank[sa1[i]];//a为按第二关键字排序后的rank 注意! 
 49         
 50         //printf("a==");rep(i,1,n) printf("%d ",a[i]);puts("");
 51          
 52         clr(sum);rep(i,1,n) sum[a[i]]++;
 53         rep(i,1,n) sum[i]+=sum[i-1];
 54         for(int i=n;i;i--) sa[sum[a[i]]--]=sa1[i];//求出新的sa 
 55         //综合第一关键字对a排序 和上面类似的奇妙操作(a按第二关键字排序了,所以这样倒着统计即可) 
 56         
 57         //printf("sa==");rep(i,1,n) printf("%d ",sa[i]);puts("");
 58     
 59         cnt=0;
 60         rep(i,1,n) a[i]=rank[i];//将rank的值保存出来 
 61         rep(i,1,n){
 62             rank[sa[i]]=(a[sa[i-1]]==a[sa[i]]&&a[sa[i-1]+l]==a[sa[i]+l])?cnt:++cnt;
 63         }//计算出新的rank 此处要利用rank考虑字符串相同的情况 
 64         
 65         //printf("rank==");rep(i,1,n) printf("%d ",rank[i]);puts("");
 66         //puts("");
 67         
 68         if(cnt==n) break;//cnt==n即排序完成
 69     }
 70     /*rep(i,1,n){
 71         printf("%d",rank[i]);
 72         putchar(i==n?'\n':' ');
 73     }*/
 74     rep(i,1,n){
 75         printf("%d",sa[i]);
 76         putchar(i==n?'\n':' ');
 77     }
 78 }
 79 void cal_Height(){//height[i]=suffix(sa[i-1])和suffix(sa[i])的最大公共前缀 
 80     //suffix(i)和suffix(j)的最大公共前缀为min(height[rank[i]+1],height[rank[i]+2]...height[rank[j]]) 
 81     int k=0;
 82     rep(i,1,n){
 83         if(k) k--;
 84         int j=sa[rank[i]-1];
 85         while(s[i+k]==s[j+k]) k++;
 86         height[rank[i]]=k;
 87     }//利用h数组(h[i]=height[rank[i]])的性质(h[i]>=h[i-1]-1)(可证)计算height 
 88     rep(i,2,n){
 89         printf("%d",height[i]);
 90         putchar(i==n?'\n':' ');
 91     }
 92 }
 93 void init(){
 94     scanf("%s",s+1);n=strlen(s+1);
 95     rep(i,1,n) sum[int(s[i])]=1;
 96     rep(i,1,m) sum[i]+=sum[i-1];
 97     rep(i,1,n) s[i]=sum[s[i]-1]+1;//将字符串转变为从1开始的整数 
 98 }
 99 int main(){
100     //freopen("#input.txt","r",stdin);
101     //freopen("#output.txt","w",stdout);
102     init();
103     cal_SA();
104     cal_Height();
105     //fclose(stdin);
106     //fclose(stdout);
107     return 0;
108 }
View Code

 

posted @ 2017-11-20 22:03  ChenThree  阅读(134)  评论(0编辑  收藏  举报