poj 1509
求一个字符串在旋转置换群下最小字典表示。
用的是后缀数组(后缀自动机还是再听听jason_yu讲讲吧,关于right集合的部分还有问题)
最小表示法的思想很有好(判断两个对象在某一置换群划分下,是否等价,可以求出两个对象在该置换群划分下的最小表示,然后比较最小表示)
1 #include <cstdio> 2 #include <cstring> 3 #define min(a,b) ((a)<(b)?(a):(b)) 4 #define N 20010 5 6 int n, aa[N]; 7 int sa[N], rk[N], ht[N], vv[N]; 8 9 void expand( int *s, int *r, int *sa, int *rk, int k ) { 10 for( int i=1; i<=n; i++ ) 11 vv[r[s[i]]]=i; 12 for( int i=n; i>=1; i-- ) 13 if( s[i]>k ) sa[vv[r[s[i]-k]]--] = s[i]-k; 14 for( int i=n-k+1; i<=n; i++ ) 15 sa[vv[r[i]]--] = i; 16 for( int i=1; i<=n; i++ ) 17 rk[sa[i]] = rk[sa[i-1]]+(r[sa[i]]!=r[sa[i-1]]||r[sa[i]+k]!=r[sa[i-1]+k]); 18 } 19 void calcht() { 20 for( int i=1,k=0; i<=n; i++ ) { 21 if( rk[i]==1 ) { 22 k = ht[i] = 0; 23 continue; 24 } 25 int j=sa[rk[i]-1]; 26 while( aa[i+k]==aa[j+k] ) k++; 27 ht[i] = k; 28 if( k>0 ) k--; 29 } 30 } 31 void suffix() { 32 static int tsa[N], trk[N]; 33 for( int i=1; i<=26; i++ ) vv[i] = 0; 34 for( int i=1; i<=n; i++ ) vv[aa[i]]++; 35 for( int i=1; i<=26; i++ ) vv[i]+=vv[i-1]; 36 for( int i=n; i>=1; i-- ) sa[vv[aa[i]]--]=i; 37 for( int i=1; i<=n; i++ ) 38 rk[sa[i]] = rk[sa[i-1]]+(aa[sa[i]]!=aa[sa[i-1]]); 39 for( int k=1; k<n; k<<=1 ) { 40 expand( sa, rk, tsa, trk, k ); 41 for( int i=1; i<=n; i++ ) 42 sa[i]=tsa[i], rk[i]=trk[i]; 43 } 44 calcht(); 45 } 46 int sov() { 47 int nn = (n+1)>>1; 48 for( int i=1; i<=n; i++ ) 49 if( n-sa[i]+1>=nn ) { 50 int rt = sa[i]; 51 for( int j=i+1; j<=n && ht[sa[j]]>=nn; j++ ) 52 rt = min( rt, sa[j] ); 53 return rt; 54 } 55 return -1; 56 } 57 int main() { 58 int T; 59 scanf( "%d", &T ); 60 while( T-- ) { 61 static char buf[N]; 62 scanf( "%s", buf+1 ); 63 n = strlen( buf+1 ); 64 for( int i=1; i<=n; i++ ) 65 aa[i+n] = aa[i] = buf[i]-'a'+1; 66 n += n; 67 suffix(); 68 printf( "%d\n", sov() ); 69 } 70 }