后缀自动机
参考
http://blog.sina.com.cn/s/blog_70811e1a01014dkz.html
http://blog.csdn.net/cyendra/article/details/37993603?utm_source=tuicool
"一个构造好的 SAM 实际上包含了两个图:由 go 数组组成的 DAG 图;由 par 指针构成的 parent 树."
设 $x$ 为自动机上一个节点(状态). 设 $s(x)$ 为 $x$ 所代表的所有子串的集合.
构造信息:
$f(x)$ : 一个满足$s(f(x)) subset s(x)$ 的 $len$ 最大的节点.
同时也是逆序后缀树的父节点指针.
$len(x)$ : $max(|s(x)|)$. 即节点 $x$ 所匹配的最长子串长度.
扩展信息:
$m(x)$ : $min(|s(x)|)$. 即 $x$ 所匹配的最短子串长度.
$l(x)$ : $l(x)=len(x)-m(x)+1$ ,表示状态x所匹配的子串个数. 每个长度的子串都统计了恰好一次.
$c(x)$ : 状态x所匹配的子串在原字符串中的出现次数. 每个 $x$ 所代表的子串均出现了这么多次.
扩展信息的求法:
用 $end$ 表示自动机的结束节点(唯一一个接受态).用 $st$ 表示自动机起点.
$m(x)$ : 按照 $m(x)$ 的性质, $m(x)=len(f(x))+1$ .
$l(x)$ : 算出 $m(x)$ 之后按照 $l(x)=len(x)-m(x)+1$ 扫一遍.
$c(x)$ : 逆拓扑序DP/记忆化搜索. $c(x)=sum{c(s)}; , ; c(end)=1$ ,其中 $s$ 是$x$ 的后继结点.
设 $s=xv$ , $v$ 是一个字符, $c(s)=c(xv)$ .$x$ 的出现次数就是 $xv$ 出现次数的和.
AC VIJOS 1567 字串计数. 非常裸的后缀数据结构....
DP用DFS莫名爆栈,一怒之下直接上stl的queue拓扑序DP.......
1 #include <cstdio>
2 #include <fstream>
3 #include <iostream>
4
5 #include <cstdlib>
6 #include <cstring>
7 #include <algorithm>
8 #include <cmath>
9
10 #include <queue>
11 #include <vector>
12 #include <map>
13 #include <set>
14 #include <stack>
15 #include <list>
16
17 typedef unsigned int uint;
18 typedef long long int ll;
19 typedef unsigned long long int ull;
20 typedef double db;
21
22 using namespace std;
23
24 inline int getint()
25 {
26 int res=0;
27 char c=getchar();
28 bool mi=false;
29 while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
30 while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
31 return mi ? -res : res;
32 }
33 inline ll getll()
34 {
35 ll res=0;
36 char c=getchar();
37 bool mi=false;
38 while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
39 while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
40 return mi ? -res : res;
41 }
42
43 db eps=1e-80;
44 inline bool feq(db a,db b)
45 { return fabs(a-b)<eps; }
46
47 template<typename Type>
48 inline Type avg(const Type a,const Type b)
49 { return a+((b-a)/2); }
50
51 //===================================================================
52 //===================================================================
53 //===================================================================
54 //===================================================================
55
56
57 struct SAM
58 {
59 struct node
60 {
61 node*f;
62 node*s[26];
63 int v;
64 ll cnt;
65 int intake;
66 node(const node*f)
67 { memcpy(this,f,sizeof(node)); }
68 node(int _v)
69 { f=NULL; memset(s,0,sizeof(s)); v=_v; cnt=-1; intake=-1; }
70 };
71
72 node*root,*cur;
73
74 SAM(){root=cur=new node(0);}
75
76 void expend(int v) //add a value v to the tail of current string.
77 {
78 node*p=cur; //the last acceptance node.
79 cur=new node(p->v+1); //this is the new node.
80
81 while(p && !p->s[v])
82 p->s[v]=cur, p=p->f; //change transfers of portion of nodes.
83
84 //stopping at this point makes no mistake.
85
86 if(!p) cur->f=root; //all nodes' transfers changed.
87 else
88 {
89 node*q=p->s[v]; //node that causes conflict.
90 if(p->v+1==q->v) cur->f=q; //edit straight.
91 else
92 {
93 node*h=new node(q); //new node for conflict resolution.
94 h->v=p->v+1; //edit straight.
95 cur->f=q->f=h;
96 while(p && p->s[v]==q) //as our new node replaced the node q,
97 p->s[v]=h, p=p->f; //transfers should be changed.
98 }
99 }
100 }
101
102 ll DFS(node*x)
103 {
104 if(x->cnt!=-1) return x->cnt;
105 ll res=0;
106 for(int i=0;i<26;i++)
107 if(x->s[i] && x->s[i]!=root) res+=DFS(x->s[i]);
108 return x->cnt=res+(x!=root);
109 }
110
111 void BFS()
112 {
113 queue<node*> q;
114 stack<node*> r;
115
116 q.push(root);
117 root->intake=0;
118 while(!q.empty()) //get degrees.
119 {
120 node*x=q.front(); q.pop();
121 for(int i=0;i<26;i++)
122 if(x->s[i])
123 {
124 node*y=x->s[i];
125 if(y->intake==-1)
126 {
127 y->intake=0;
128 q.push(y);
129 }
130 y->intake++;
131 }
132 }
133
134 q.push(root);
135 while(!q.empty()) //topo sort
136 {
137 node*x=q.front(); q.pop();
138 r.push(x);
139 for(int i=0;i<26;i++)
140 if(x->s[i])
141 {
142 node*y=x->s[i];
143 y->intake--;
144 if(y->intake==0)
145 q.push(y);
146 }
147 }
148
149 while(!r.empty()) //dp
150 {
151 node*x=r.top(); r.pop();
152 x->cnt=1;
153 for(int i=0;i<26;i++)
154 if(x->s[i]) x->cnt+=x->s[i]->cnt;
155 }
156 }
157
158 };
159
160
161 int n;
162
163 char a[205000];
164
165 int main()
166 {
167 SAM T;
168 n=getint();
169 for(int i=0;i<n;i++)
170 {
171 char c=getchar();
172 while(c<'a' || 'z'<c) c=getchar();
173 a[i]=c;
174 T.expend(c-'a');
175 }
176
177 T.BFS();
178
179 printf("%I64d
",T.root->cnt-1);
180
181
182 return 0;
183 }
DAGDP,用 $cnt(x)$ 表示,从节点x出发,能走出的路径的条数.
那么直接在DAG上做DP, $cnt(x)=1+cnt(s)$ . $s$ 是 $x$ 的后继状态.
最后的答案要减去从root走到root(空串)的一个方案.
另外,如果是能走到终止状态的路径条数,那么显然是字符串的长度 $n$ ,因为每一条走到终止状态的路径都代表了原串的唯一一个后缀.
AC SPOJ 1811 (LCS) 最长公共子串
1 #include <cstdio>
2 #include <fstream>
3 #include <iostream>
4
5 #include <cstdlib>
6 #include <cstring>
7 #include <algorithm>
8 #include <cmath>
9
10 #include <queue>
11 #include <vector>
12 #include <map>
13 #include <set>
14 #include <stack>
15 #include <list>
16
17 typedef unsigned int uint;
18 typedef long long int ll;
19 typedef unsigned long long int ull;
20 typedef double db;
21
22 using namespace std;
23
24 inline int getint()
25 {
26 int res=0;
27 char c=getchar();
28 bool mi=false;
29 while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
30 while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
31 return mi ? -res : res;
32 }
33 inline ll getll()
34 {
35 ll res=0;
36 char c=getchar();
37 bool mi=false;
38 while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
39 while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
40 return mi ? -res : res;
41 }
42
43 db eps=1e-80;
44 inline bool feq(db a,db b)
45 { return fabs(a-b)<eps; }
46
47 template<typename Type>
48 inline Type avg(const Type a,const Type b)
49 { return a+((b-a)/2); }
50
51 //===================================================================
52 //===================================================================
53 //===================================================================
54 //===================================================================
55
56
57 int s[500050][26];
58 int f[500050];
59 int len[500050];
60 int dep[500050];
61 int nt;
62 int root;
63 int cur;
64
65 int newnode(int l)
66 { memset(s[nt],0,sizeof(s[nt]));f[nt]=0;len[nt]=l;return nt++; }
67
68 void Expend(int v)
69 {
70 int p=cur;
71 cur=newnode(len[p]+1);
72
73 while(p && !s[p][v])
74 s[p][v]=cur,p=f[p];
75
76 if(!p) f[cur]=root;
77 else
78 {
79 int q=s[p][v];
80 if(len[p]+1==len[q]) f[cur]=q;
81 else
82 {
83 int h=newnode(len[p]+1);
84 memcpy(s[h],s[q],sizeof(s[h]));
85 f[h]=f[q];
86
87 f[cur]=f[q]=h;
88 while(p && s[p][v]==q)
89 s[p][v]=h,p=f[p];
90 }
91 }
92 }
93
94 int n;
95
96 char a[250050];
97 char b[250050];
98
99
100 int main()
101 {
102 nt=1;
103 root=cur=newnode(0);
104
105 scanf("%s%s",a,b);
106 n=strlen(a);
107 for(int i=0;i<n;i++)
108 Expend(a[i]-'a');
109
110 n=strlen(b);
111 int res=0;
112 int ans=0;
113 int x=root;
114 for(int i=0;i<n;i++)
115 {
116 int v=b[i]-'a';
117
118 if(s[x][v]) res++,x=s[x][v];
119 else
120 {
121 while(x && !s[x][v]) x=f[x];
122 if(x) res=len[x]+1,x=s[x][v];
123 else res=0,x=root;
124 }
125
126 ans=max(ans,res);
127 }
128
129 printf("%d
",ans);
130
131 return 0;
132 }
注意len的意义. 如果匹配,直接把统计值加1.
如果失配,那么就要依次考察f(x),f(f(x))....
注意,当我们匹配到状态 $x$ 的时候,实际上我们已经匹配了一大堆字符串的后缀.
设当前需要匹配的字符为 $x$ ,如果 $x$ 的 $c$ 转移不存在,说明失配了.
那么我们通过 $f$ 转移,就意味着不断缩短当前匹配串的长度,这个时候会有新的已匹配串加入进来,它们可能可以匹配 $x$ .
如果找不到这样的状态说明当前没法转移了,直接把数据初始化.
AC SPOJ 8222 Substrings
1 #include <cstdio>
2 #include <fstream>
3 #include <iostream>
4
5 #include <cstdlib>
6 #include <cstring>
7 #include <algorithm>
8 #include <cmath>
9
10 #include <queue>
11 #include <vector>
12 #include <map>
13 #include <set>
14 #include <stack>
15 #include <list>
16
17 typedef unsigned int uint;
18 typedef long long int ll;
19 typedef unsigned long long int ull;
20 typedef double db;
21
22 using namespace std;
23
24 inline int getint()
25 {
26 int res=0;
27 char c=getchar();
28 bool mi=false;
29 while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
30 while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
31 return mi ? -res : res;
32 }
33 inline ll getll()
34 {
35 ll res=0;
36 char c=getchar();
37 bool mi=false;
38 while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
39 while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
40 return mi ? -res : res;
41 }
42
43 db eps=1e-80;
44 inline bool feq(db a,db b)
45 { return fabs(a-b)<eps; }
46
47 template<typename Type>
48 inline Type avg(const Type a,const Type b)
49 { return a+((b-a)/2); }
50
51 //===================================================================
52 //===================================================================
53 //===================================================================
54 //===================================================================
55
56
57 int s[500050][26];
58 int f[500050];
59 int len[500050];
60 int dep[500050];
61 int c[505000];
62 int nt;
63 int root;
64 int cur;
65
66 int newnode(int l)
67 { memset(s[nt],0,sizeof(s[nt]));f[nt]=0;len[nt]=l;return nt++; }
68
69 void Expend(int v)
70 {
71 int p=cur;
72 cur=newnode(len[p]+1);
73
74 while(p && !s[p][v])
75 s[p][v]=cur,p=f[p];
76
77 if(!p) f[cur]=root;
78 else
79 {
80 int q=s[p][v];
81 if(len[p]+1==len[q]) f[cur]=q;
82 else
83 {
84 int h=newnode(len[p]+1);
85 memcpy(s[h],s[q],sizeof(s[h]));
86 f[h]=f[q];
87
88 f[cur]=f[q]=h;
89 while(p && s[p][v]==q)
90 s[p][v]=h,p=f[p];
91 }
92 }
93 }
94
95 int n;
96 int v[500050];
97 int p[500050];
98 int r[500050];
99 char a[250050];
100
101
102 int main()
103 {
104 nt=1;
105 root=cur=newnode(0);
106
107 scanf("%s",a);
108 n=strlen(a);
109 for(int i=0;i<n;i++)
110 Expend(a[i]-'a');
111
112 int x=root;
113 int i=0;
114 do { x=s[x][a[i++]-'a']; if(x)c[x]=1; } while(x);
115
116 for(int i=1;i<nt;i++) v[len[i]]++;
117 for(int i=1;i<=n;i++) v[i]+=v[i-1];
118 for(int i=1;i<nt;i++) p[v[len[i]]--]=i;
119 for(int i=nt-1;i>=1;i--)
120 r[len[p[i]]]=max(r[len[p[i]]],c[p[i]]),c[f[p[i]]]+=c[p[i]];
121 for(int i=1;i<=n;i++) printf("%d
",r[i]);
122
123 return 0;
124 }
对DP方程的理解参照 http://blog.csdn.net/huyuncong/article/details/7583214
用逆序后缀树的话叶子数就是出现次数.
基数排序实际上就是保证了先更新子节点再更新父节点.因为叶子节点的 $len$ 总是会比父亲大.
后缀数组
AC BZOJ 1031
1 int n,m;
2 int s[405000];
3 int p[405000];
4 int r[405000];
5 int h[405000];
6
7 int step;
8 bool cmpr(const int&a,const int&b)
9 { return r[a]==r[b] ? r[a+step]<r[b+step] : r[a]<r[b]; }
10 bool cmps(const int&a,const int&b)
11 { return s[a]<s[b]; }
12
13 void GetRank(int*f,int*x,int*v)
14 //f:resault array; x:pointer array; v:value array.
15 {
16 int t=1;
17 f[x[0]]=t;
18 for(int i=1;i<n;i++)
19 {
20 if(v[x[i]]!=v[x[i-1]] || v[x[i]+step]!=v[x[i-1]+step]) t++;
21 f[x[i]]=t;
22 }
23 }
24
25 void Build()
26 {
27 for(int i=0;i<n;i++) p[i]=i;
28 step=0;
29 sort(p,p+n,cmps);
30 GetRank(r,p,s);
31 for(step=1;step<=n;step<<=1)
32 {
33 sort(p,p+n,cmpr);
34 memcpy(h,r,sizeof(int)*n);
35 GetRank(r,p,h);
36 }
37 }
38
39 char inp[105000];
40 int res[105000];
41
42 int main()
43 {
44 int ip=0;
45 char c=getchar();
46 while(c!='
' && !feof(stdin)) inp[ip++]=c,c=getchar();
47
48 n=strlen(inp);
49 for(int i=0;i<n;i++)
50 s[i]=inp[i];
51 memcpy(s+n,s,sizeof(int)*n);
52 n<<=1;
53
54 Build();
55
56 n>>=1;
57
58 int rt=0;
59 for(int i=0;i<(n<<1);i++)
60 if(p[i]<n) res[rt++]=s[p[i]+n-1];
61
62 for(int i=0;i<n;i++) printf("%c",(char)res[i]);
63 printf("
");
64
65 return 0;
66 }
O(nlog2n)的算法. 倍增+快速排序.
本地跑极限数据2s.....交上去1.8s水过......
思路在程序里写得很清楚了...
先求出长度 2n-1 的所有字串的rank....
再使用rank作为关键字(双关键字,两个关键字都在rank数组中)排序,
然后用排序了的指针再求出 2n 的所有字串的rank.....
这样倍增地排序直到长度 2n >len .....
就行啦....
妈呀计数排序居然可以这么玩!!
1 int n,m;
2
3 int _v[5005000];
4 int _r[5005000];
5 int _p[5005000];
6 void CountingSort(int*p,int*fv,int*sv,int len)
7 {
8 //second key sort.
9 memset(_v,0,sizeof(int)*(m+1));
10 for(int i=0;i<len;i++) _v[sv[i]]++;
11 for(int i=1;i<m;i++) _v[i]+=_v[i-1];
12 for(int i=len-1;i>=0;i--) _p[--_v[sv[i]]]=i;
13 //first key sort.
14 memset(_v,0,sizeof(int)*(m+1));
15 for(int i=0;i<len;i++) _v[fv[_p[i]]]++;
16 for(int i=1;i<m;i++) _v[i]+=_v[i-1];
17 for(int i=len-1;i>=0;i--) p[--_v[fv[_p[i]]]]=_p[i];
18 }
计数排序如何排双关键字呢?
先把第二关键字排序,拿到排序指针(p[i]表示第i小的元素的下标)
然后.....按照p[i]的顺序(即,以p[i]作下标代替原来的0,1..,n-1)再来一次计数排序....
下面是标准写法的SA. AC BZOJ 1031
1 #include <cstdio>
2 #include <fstream>
3 #include <iostream>
4
5 #include <cstdlib>
6 #include <cstring>
7 #include <algorithm>
8 #include <cmath>
9
10 #include <queue>
11 #include <vector>
12 #include <map>
13 #include <set>
14 #include <stack>
15 #include <list>
16
17 typedef unsigned int uint;
18 typedef long long int ll;
19 typedef unsigned long long int ull;
20 typedef double db;
21
22 using namespace std;
23
24 inline int getint()
25 {
26 int res=0;
27 char c=getchar();
28 bool mi=false;
29 while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
30 while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
31 return mi ? -res : res;
32 }
33 inline ll getll()
34 {
35 ll res=0;
36 char c=getchar();
37 bool mi=false;
38 while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
39 while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
40 return mi ? -res : res;
41 }
42
43 db eps=1e-80;
44 inline bool feq(db a,db b)
45 { return fabs(a-b)<eps; }
46
47 template<typename Type>
48 inline Type avg(const Type a,const Type b)
49 { return a+((b-a)/2); }
50
51 //===================================================================
52 //===================================================================
53 //===================================================================
54 //===================================================================
55
56
57 int n,m=257;
58 int s[405000];
59 int p[405000];
60 int _p[405000];
61 int r[2][405000];
62 int v[405000];
63
64 int step;
65
66 void GetRank(int*f,int*x,int*v)
67 //f:resault array; x:pointer array; v:value array.
68 {
69 int t=0;
70 f[x[0]]=t;
71 for(int i=1;i<n;i++)
72 {
73 if(v[x[i]]!=v[x[i-1]] || v[x[i]+step]!=v[x[i-1]+step]) t++;
74 f[x[i]]=t;
75 }
76 }
77
78 void Build()
79 {
80 step=0;
81 int k=0;
82 for(int i=0;i<n;i++) v[s[i]]++;
83 for(int i=1;i<m;i++) v[i]+=v[i-1];
84 for(int i=0;i<n;i++) p[--v[s[i]]]=i;
85 GetRank(r[!k],p,s); k=!k;
86 for(step=1;step<=n;step<<=1)
87 {
88 //second key sort. using pointer array p.
89 int c=0;
90 for(int i=n-step;i<n;i++) _p[c++]=i;
91 for(int i=0;i<n;i++) if(p[i]>=step) _p[c++]=p[i]-step;
92
93 //first key sort.
94 memset(v,0,sizeof(int)*(n+1));
95 for(int i=0;i<n;i++) v[r[k][i]]++;
96 for(int i=1;i<m;i++) v[i]+=v[i-1];
97 for(int i=n-1;i>=0;i--) p[--v[r[k][_p[i]]]]=_p[i];
98
99 GetRank(r[!k],p,r[k]); k=!k;
100 }
101 }
102
103 char inp[105000];
104 int res[105000];
105
106 int main()
107 {
108 int ip=0;
109 char c=getchar();
110 while(c!='
' && !feof(stdin)) inp[ip++]=c,c=getchar();
111
112 n=strlen(inp);
113 for(int i=0;i<n;i++)
114 s[i]=inp[i];
115 memcpy(s+n,s,sizeof(int)*(n+1));
116
117 n<<=1;
118 m=max(m,n+2); //for characters and ranks' public value range.
119
120 Build();
121
122 n>>=1;
123
124 int rt=0;
125 for(int i=0;i<(n<<1);i++)
126 if(p[i]<n) res[rt++]=s[p[i]+n-1];
127
128 for(int i=0;i<n;i++) printf("%c",(char)res[i]);
129 printf("
");
130
131 return 0;
132 }
再次突显常数优化重要性......用时840ms....还是打不过各路神犇TAT
再来一题,跟上边的差不多,但是多解的情况要让靠前的作为输出.
于是乎,把第二关键字排序换回原来的形式......
就可以稳定排序了0.0 下面是代码
AC VISOS 1437
1 #include <cstdio>
2 #include <fstream>
3 #include <iostream>
4
5 #include <cstdlib>
6 #include <cstring>
7 #include <algorithm>
8 #include <cmath>
9
10 #include <queue>
11 #include <vector>
12 #include <map>
13 #include <set>
14 #include <stack>
15 #include <list>
16
17 typedef unsigned int uint;
18 typedef long long int ll;
19 typedef unsigned long long int ull;
20 typedef double db;
21
22 using namespace std;
23
24 inline int getint()
25 {
26 int res=0;
27 char c=getchar();
28 bool mi=false;
29 while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
30 while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
31 return mi ? -res : res;
32 }
33 inline ll getll()
34 {
35 ll res=0;
36 char c=getchar();
37 bool mi=false;
38 while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
39 while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
40 return mi ? -res : res;
41 }
42
43 db eps=1e-80;
44 inline bool feq(db a,db b)
45 { return fabs(a-b)<eps; }
46
47 template<typename Type>
48 inline Type avg(const Type a,const Type b)
49 { return a+((b-a)/2); }
50
51 //===================================================================
52 //===================================================================
53 //===================================================================
54 //===================================================================
55
56
57 int n,m=257;
58 int s[405000];
59 int p[405000];
60
61 int step;
62
63 int GetRank(int*f,int*x,int*v)
64 {
65 int t=0;
66 f[x[0]]=t;
67 for(int i=1;i<n;i++)
68 {
69 if(v[x[i]]!=v[x[i-1]] || v[x[i]+step]!=v[x[i-1]+step]) t++;
70 f[x[i]]=t;
71 }
72 return t+1;
73 }
74
75 int r[2][405000];
76 int _p[405000];
77 int v[405000];
78 void Build(int*p,int*x,int*y)
79 {
80 step=0;
81 for(int i=0;i<n;i++) v[s[i]]++;
82 for(int i=1;i<m;i++) v[i]+=v[i-1];
83 for(int i=n-1;i>=0;i--) p[--v[s[i]]]=i;
84 m=max(m,GetRank(x,p,s));
85 for(step=1;step<=n;step<<=1)
86 {
87 //second key sort.
88 memset(v,0,sizeof(int)*(n+1));
89 for(int i=0;i<n;i++) v[x[i+step]]++;
90 for(int i=1;i<m;i++) v[i]+=v[i-1];
91 for(int i=n-1;i>=0;i--) _p[--v[x[i+step]]]=i;
92
93 //first key sort.
94 memset(v,0,sizeof(int)*(n+1));
95 for(int i=0;i<n;i++) v[x[i]]++;
96 for(int i=1;i<m;i++) v[i]+=v[i-1];
97 for(int i=n-1;i>=0;i--) p[--v[x[_p[i]]]]=_p[i];
98
99 m=max(m,GetRank(y,p,x)); swap(x,y);
100 }
101 }
102
103 char inp[205000];
104
105 int main()
106 {
107 n=getint();
108 for(int i=0;i<n;i++)
109 { char c=getchar(); while(c<'a' || 'z'<c) c=getchar(); inp[i]=c; }
110 memcpy(inp+n,inp,sizeof(char)*n);
111 n<<=1;
112
113 for(int i=0;i<n;i++) s[i]=inp[i]-'a';
114 m=27;
115
116 Build(p,r[0],r[1]);
117
118 //for(int i=0;i<n;i++)
119 //if(p[i]<n/2) { printf("%d %d:",i,p[i]); for(int j=p[i];j<p[i]+n/2;j++) printf("%c",inp[j]); printf(" "); for(int j=p[i]+n/2;j<n;j++) printf("%c",inp[j]); printf("
"); }
120
121 for(int i=0;i<n;i++)
122 if(p[i]<n/2)
123 {
124 printf("%d
",p[i]);
125 break;
126 }
127
128 return 0;
129 }
未完待续
后缀树