题目链接: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 ",sa[p][i]) ; 49 return 0 ; 50 }