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