后缀排序(后缀数组初探

  题目链接:http://codevs.cn/problem/1500/

 

 后缀数组裸题。(不清楚概念的点这里

  代码及说明:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define LL long long
 6 #define RI register int
 7 using namespace std;
 8 const int INF = 0x7ffffff ;
 9 const int N = 15000 + 10 ;
10 
11 inline int read() {
12     int k = 0 , f = 1 ; char c = getchar() ;
13     for( ; !isdigit(c) ; c = getchar())
14       if(c == '-') f = -1 ;
15     for( ; isdigit(c) ; c = getchar())
16       k = k*10 + c-'0' ;
17     return k*f ;
18 }
19 int n, k, p, q = 1 ; 
20 int a[N], v[N], sa[2][N], rk[2][N] ; // 滚动数组实现 
21 char s[N] ;
22 
23 inline void mul(int *sa,int *rk,int *SA,int *RK) {  // 前一个状态 -> 后一个状态 
24     for(int i=1;i<=n;i++) v[rk[sa[i]]] = i ;  // v[i]表示权值小于等于为i的有多少个(此时权值为上一状态中的排名)
25                                               // sa[i]的排名属于区间 ( v[rk[sa[i]]-1] , v[rk[sa[i]]] ] 
26     for(int i=n;i;i--) if(sa[i] > k) SA[v[rk[sa[i]-k]]--] = sa[i]-k ;  // 按第二关键字由大到小的顺序排序 
27     for(int i=n-k+1;i<=n;i++) SA[v[rk[i]]--] = i ;  // 把没有第二关键字的排序 
28     for(int i=1;i<=n;i++) RK[SA[i]] = RK[SA[i-1]] + (rk[SA[i]] != rk[SA[i-1]] || rk[SA[i]+k] != rk[SA[i-1]+k]) ;
29 }
30 inline void presa() {
31     for(int i=1;i<=n;i++) v[a[i]]++ ;  // 放入桶中 ( v[i]表示权值为i的有多少个 
32     for(int i=1;i<=26;i++) v[i] += v[i-1] ;  // 维护前缀和 , 则对于i,rk[i]一定在 ( v[a[i]-1] , v[a[i]] ] 中。 
33     for(int i=1;i<=n;i++) sa[p][v[a[i]]--] = i ;  // p表示当前状态,先不用管
34                                                   // sa[i]表示的是权值第i小的后缀(此时是单个字符)的第一个字符的数组下标
35     for(int i=1;i<=n;i++) rk[p][sa[p][i]] = rk[p][sa[p][i-1]] + (a[sa[p][i]] != a[sa[p][i-1]]) ; // sa是按权值排序的,若权值与前一个相同,那么排名也与前一个相同;否则 + 1 
36     for(k=1;k<n;k<<=1,swap(p,q)) {  // 倍增求sa数组(求后缀长度为k的sa数组 
37         mul(sa[p],rk[p],sa[q],rk[q]) ;  // p表示前一个状态(即后缀长度为k-1),q表示当前状态(即后缀长度为k) 
38         if(rk[p][sa[q][n]] == n) {   // 剪枝 (第n小的排名为n证明前面没有相同的,则无需进行下去 
39             swap(p,q) ; return ;
40         }
41     }
42 }
43 
44 int main() {
45     n = read() ; scanf("%s",s+1) ;
46     for(int i=1;i<=n;i++) a[i] = s[i]-'a'+1 ;  
47     presa() ; // 获得sa数组 
48     for(int i=1;i<=n;i++) printf("%d\n",sa[p][i]) ;
49     return 0 ;
50 }

 

posted @ 2018-03-28 16:34  zubizakeli  阅读(250)  评论(0编辑  收藏  举报