题目链接:http://poj.org/problem?id=1509
题目意思就是求循环字符串的最小表示。
我们用字符串S+S建立SAM,然后从root开始走n步,每次尽量选最小的。
由于 SAM 可以接受 SS 所有的子串,而字典序最小的字符串也必定是 SS 的子串,因此按照上面的规则移动就可以找到一个字典序最小的子串。
这里的right等价类的len显然可以直接扩展到串首,于是开始的位置就是T[o].len-n+1。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 struct State{ 6 int ch[26],fa,len; 7 void init(){ 8 fa=-1; 9 len=0; 10 memset(ch,-1,sizeof(ch)); 11 } 12 }T[50010]; 13 char s[10010]; 14 int cnt=0,la; 15 void Extend(int c){ 16 int end=++cnt,tmp=la; 17 T[end].init(); 18 T[end].len=T[tmp].len+1; 19 while(tmp!=-1&&T[tmp].ch[c]==-1){ 20 T[tmp].ch[c]=end; 21 tmp=T[tmp].fa; 22 } 23 if(!~tmp) T[end].fa=0; 24 else{ 25 int ne=T[tmp].ch[c]; 26 if(T[tmp].len+1==T[ne].len) T[end].fa=ne; 27 else{ 28 int np=++cnt; 29 T[np].init(); 30 T[np]=T[ne]; 31 T[np].len=T[tmp].len+1; 32 T[end].fa=T[ne].fa=np; 33 while(tmp!=-1&&T[tmp].ch[c]==ne){ 34 T[tmp].ch[c]=np; 35 tmp=T[tmp].fa; 36 } 37 } 38 } 39 la=end; 40 } 41 int main(){ 42 int Test; 43 scanf("%d",&Test); 44 while(Test--){ 45 cnt=0; 46 la=0; 47 T[0].init(); 48 scanf("%s",s); 49 int n=strlen(s); 50 for(int i=0;i<n;i++) Extend(s[i]-'a'); 51 for(int i=0;i<n;i++) Extend(s[i]-'a'); 52 int o=0; 53 for(int i=0;i<n;i++) 54 for(int j=0;j<26;j++) 55 if(~T[o].ch[j]){ 56 o=T[o].ch[j]; 57 break; 58 } 59 printf("%d\n",T[o].len-n+1); 60 } 61 return 0; 62 }