首页 > 技术文章 > [POJ1509]Glass Beads 后缀自动机 最小循环串

halfrot 2017-08-28 19:34 原文

题目链接: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 }

 

推荐阅读