bzoj 2251

 

第一道后缀数组

 

后缀数组要维护三个数组:sa(suffix array), rk(rank)和ht(height)。

含义分别是:

sa[i]:将后缀按照字典序排序后,第i大的后缀的起始位置。

rk[i]:起始位置为i的后缀的排名。

ht[i]:起始位置为i的后缀与排名为rk[i]-1的后缀的最长公共前缀。

 

对于任意一个字串,一定是某个后缀的前缀。

然后从sa[1]开始,统计字串,每个后缀sa[i]的可能的字串的个数(不与前面统计的重复)是:n-sa[i]+1-ht[sa[i]]

然后就这羊枚举子串,幷向后暴力统计个数,这样还能保证字典序是从小到大。

 

 1 /**************************************************************
 2     Problem: 2251
 3     User: idy002
 4     Language: C++
 5     Result: Accepted
 6     Time:240 ms
 7     Memory:892 kb
 8 ****************************************************************/
 9  
10 #include <cstdio>
11 #define maxn 3010
12  
13 int n;
14 int aa[maxn];
15 int sa[2][maxn], rk[2][maxn], ht[maxn], vv[maxn], p;
16 char str[maxn];
17  
18 void expand( int k, int sa[maxn], int rk[maxn], int tsa[maxn], int trk[maxn] ) {
19     for( int i=1; i<=n; i++ ) vv[rk[sa[i]]]=i;
20     for( int i=n; i>=1; i-- ) if( sa[i]>k ) tsa[vv[rk[sa[i]-k]]--]=sa[i]-k;
21     for( int i=n-k+1; i<=n; i++ ) tsa[vv[rk[i]]--]=i;
22     for( int i=1; i<=n; i++ ) trk[tsa[i]]=trk[tsa[i-1]]+(rk[tsa[i]]!=rk[tsa[i-1]]||rk[tsa[i]+k]!=rk[tsa[i-1]+k]);
23 }
24 void makeht() {
25     int k=0;
26     ht[sa[p][1]] = 0;
27     for( int i=1; i<=n; i++ ) {
28         if( rk[p][i]==1 ) continue;
29         int j=sa[p][rk[p][i]-1];
30         while( aa[i+k]==aa[j+k] ) k++;
31         ht[i] = k;
32         if( k>0 ) k--;
33     }
34 }
35 void suffix() {
36     p=0;
37     for( int i=1; i<=n; i++ ) vv[aa[i]]++;
38     for( int i=1; i<=2; i++ ) vv[i]+=vv[i-1];
39     for( int i=n; i>=1; i-- ) sa[p][vv[aa[i]]--]=i;
40     for( int i=1; i<=n; i++ ) rk[p][sa[p][i]]=rk[p][sa[p][i-1]]+(aa[sa[p][i]]!=aa[sa[p][i-1]]);
41     for( int k=1; k<n; k<<=1,p=1-p ) expand( k, sa[p], rk[p], sa[1-p], rk[1-p] );
42     makeht();
43 }
44  
45 int main() {
46     scanf( "%d", &n );
47     scanf( "%s", str+1 );
48     for( int i=1; i<=n; i++ )
49         aa[i] = str[i]-'0'+1;
50     suffix();
51     for( int i=1; i<=n; i++ ) {
52         for( int t=sa[p][i]+ht[sa[p][i]]; t<=n; t++ ) {
53             int cnt=1;
54             for( int j=i+1; j<=n && ht[sa[p][j]]>=(t-sa[p][i]+1); j++,cnt++);
55             if( cnt>1 )
56                 printf( "%d\n", cnt );
57         }
58     }
59 }
60 
View Code

 

posted @ 2015-03-17 16:08  idy002  阅读(184)  评论(0编辑  收藏  举报