SPOJ LCS Longest Common Substring (后缀自动机模板题)
题意:
给出2个字符串(2.5E5),问最长公共子串的长度。
思路:
拿一个串建SAM
由于SAM上的任何一个状态,都对应一个或者若干个子串,然后拿另一个串在SAM上面跑就行了
20171110UPdate:我好像说得太简略了。。
参考clj的冬令营讲稿:
* 给两个长度小于100000的字符串A和B,求出他们的最长公共连续子串。 Ê我们构造出A的后缀自动机SAM * 对于SAM的每个状态s,令r为Right(s)中任意的一个元素,它代表的是结束位置在r的,长度在[Min(s),Max(s)]之间的所有子串。 * 我们不妨对状态s,求出所有B的子串中,从任意r开始往前能匹配的最大长度L,那么min(L,Max(s))就可以更新答案了。 * 我们考虑用_SAM_读入字符串_B_。 * **令当前状态为_s_,同时最大匹配长度为_len _Ê我们读入字符_x_。如果_s_有标号为_x_的边,** * **那么_s=trans(s,x),len = len+1 _Ê否则我们找到_s_的第一个祖先_a_,它有标号为_x_的边,令** * **_s=trans(a,x),len=Max(a)+1__。 _Ê如果没有这样的祖先,那么令_s=root,len=0_。** * 在过程中更新状态的最大匹配长度
唯一不显然的地方在于,为什么当失配时,我们是沿着parent树向上找。
考虑如下例子:
我们知道,parent树,或者叫后缀链树,一个状态S指向的是,总起点开始到S的子串的后缀中,长度最长的后缀,满足该后缀所对应的某个状态Q(对应的意思是说,从初始状态到状态Q表示的子串恰好是该后缀)的right集合和状态S的集合不相等。
假设我们目前匹配到的状态表示的子串是abcbc,然后下一位要匹配的字母是b,而状态abcbc没有通过b转移的边(这些图上没有,我懒得画了orz)
这个时候考虑回退…对于abcbc的后缀,bcbc,cbc和abcbc面临的情况是一样的,因为bcbc,cbc,abcbc三个字符串的right集合相同,也就是出现的位置完全相同,那么既然abcbc没有通过b转移的边,bcbc,cbc也一定没有通过b转移的边。
这时候考虑转移到abcbc的par状态,也就是表示子串bc的状态,原因是bc的right集合和abcbc的right集合不同,更准确得说,bc的right集合是包含abcbc的right集合的。
而bc在之前匹配abcbc的时候,已经匹配过了,也就是说bc一定是可以匹配的,
如果此时bc有一条通过b转移的边,此时匹配的长度就变成了bc对应的状态所对应的max再+1
老年人的第一个SAM
关于SAM的笔记之后补。
用了动态的写法.
1/* ***********************************************
2Author :111qqz
3Created Time :2017年11月03日 星期五 18时20分42秒hongchenzuoban
4File Name :2774_SAM.cpp
5************************************************ */
6
7#include <bits/stdc++.h>
8#define PB push_back
9#define fst first
10#define sec second
11#define lnxt l,m,rt<<1
12#define rnxt m+1,r,rt<<1|1
13#define ms(a,x) memset(a,x,sizeof(a))
14typedef long long LL;
15#define pi pair < int ,int >
16#define MP make_pair
17
18using namespace std;
19const double eps = 1E-8;
20const int dx4[4]={1,0,0,-1};
21const int dy4[4]={0,-1,1,0};
22const int inf = 0x3f3f3f3f;
23const int N=3E5;
24struct SAM
25{
26 struct node
27 {
28 node *nxt[26],*fa;int len;
29 node(int M) {len=M;fa=0;memset(nxt,0,sizeof(nxt));}
30 };
31 typedef node* P_node;
32 P_node ro,lst;
33 SAM() {ro=new node(0);lst=ro;}
34 void Insert(char ch)
35 {
36 int ID=ch-'a';P_node p=lst,np=new node(lst->len+1);
37 while (p&&!p->nxt[ID]) p->nxt[ID]=np,p=p->fa;
38 if (!p) np->fa=ro; else
39 {
40 P_node q=p->nxt[ID];
41 if (p->len+1==q->len) np->fa=q; else
42 {
43 P_node nq=new node(p->len+1);
44 memcpy(nq->nxt,q->nxt,sizeof(q->nxt));
45 nq->fa=q->fa;q->fa=nq;np->fa=nq;
46 while (p&&p->nxt[ID]==q) p->nxt[ID]=nq,p=p->fa;
47 }
48 }
49 lst=np;
50 }
51 void make_SAM(char *s) {for (int i=0; s[i]!='\0';i++) Insert(s[i]);}
52 int LCS(char *s) //求此后缀自动机对应字符串与s的LCS
53 {
54 int ans=0,len=0;P_node p=ro;
55 for (int i=0;s[i]!='\0';i++)
56 {
57 int ID=s[i]-'a';
58 if (p->nxt[ID]) p=p->nxt[ID],len++; else
59 {
60 while (p&&!p->nxt[ID]) p=p->fa;
61 if (!p) p=ro,len=0; else len=p->len+1,p=p->nxt[ID];
62 }
63 ans=max(ans,len); //从所有len中刷出最优解
64 }
65 return ans;
66 }
67};
68SAM sam;
69char A[N],B[N];
70int main()
71{
72 #ifndef ONLINE_JUDGE
73 freopen("./in.txt","r",stdin);
74 #endif
75 scanf("%s %s",A,B);
76// cout<<A<<endl;
77// cout<<B<<endl;
78 sam.make_SAM(A);
79 int ans = sam.LCS(B);
80 printf("%d\n",ans);
81
82 #ifndef ONLINE_JUDGE
83 fclose(stdin);
84 #endif
85 return 0;
86}
不过还是比较喜欢写静态的?
参考了zqc大爷的代码风格:
1/* ***********************************************
2Author :111qqz
3Created Time :2017年11月03日 星期五 18时20分42秒
4File Name :2774_SAM.cpp
5************************************************ */
6
7#include <bits/stdc++.h>
8#define PB push_back
9#define fst first
10#define sec second
11#define lnxt l,m,rt<<1
12#define rnxt m+1,r,rt<<1|1
13#define ms(a,x) memset(a,x,sizeof(a))
14typedef long long LL;
15#define pi pair < int ,int >
16#define MP make_pair
17
18using namespace std;
19const double eps = 1E-8;
20const int dx4[4]={1,0,0,-1};
21const int dy4[4]={0,-1,1,0};
22const int inf = 0x3f3f3f3f;
23
24const int maxn = 5E5;
25
26struct node{
27 node*nxt[26],*fail;
28 LL len,cnt;
29 int ind;
30 int occ;
31};
32struct SAM{
33 node no[maxn];
34 node*root;
35 int cnt;
36 node*newnode(){
37 return &no[cnt++];
38 }
39 SAM(){
40 cnt = 0;
41 root = newnode();
42 }
43 node*add(int c,node*p){
44 node*cur = newnode();
45 cur->len = p->len+1;
46 while(p&&!p->nxt[c]){
47 p->nxt[c] = cur;
48 p = p->fail;
49 }
50 if(!p){
51 cur->fail = root;
52 return cur;
53 }
54 node*q = p->nxt[c];
55 if(p->len+1==q->len){
56 cur->fail = q;
57 }else{
58 node*nq = newnode();
59 *nq = *q;
60 q->fail = cur->fail = nq;
61 nq->len = p->len+1;
62 while(p&&p->nxt[c]==q){
63 p->nxt[c] = nq;
64 p = p->fail;
65 }
66 }
67 return cur;
68 }
69 int LCS(string s)
70 {
71// cout<<"S:"<<s<<endl;
72 int ans=0,len=0;
73 node *p=root;
74 for ( auto i:s)
75 {
76 int ID = i-'a';
77 //cout<<"id:"<<ID<<" s[i]:"<<i<<" i:"<<i<<endl;
78 if (p->nxt[ID]) p=p->nxt[ID],len++;
79 else
80 {
81 while (p&&!p->nxt[ID]) p=p->fail;
82 if (!p) p=root,len=0;else len=p->len+1,p=p->nxt[ID];
83 }
84 ans = max(ans,len);
85 }
86 return ans;
87 }
88};
89SAM sam;
90string A,B;
91int main()
92{
93 #ifndef ONLINE_JUDGE
94// freopen("./in.txt","r",stdin);
95 #endif
96 node *cur =sam.root;
97 cin>>A>>B;
98 for ( auto i:A) cur = sam.add(i-'a',cur);
99 int ans = sam.LCS(B);
100 printf("%d\n",ans);
101
102 #ifndef ONLINE_JUDGE
103 fclose(stdin);
104 #endif
105 return 0;
106}
107
108
109
110
111
112
113
114
115/* ***********************************************
116Author :111qqz
117Created Time :2017年11月15日 星期三 19时06分15秒
118File Name :SAM.cpp
119************************************************ */
120
121#include <bits/stdc++.h>
122#define PB push_back
123#define fst first
124#define sec second
125#define lson l,m,rt<<1
126#define rson m+1,r,rt<<1|1
127#define ms(a,x) memset(a,x,sizeof(a))
128typedef long long LL;
129#define pi pair < int ,int >
130#define MP make_pair
131
132using namespace std;
133const double eps = 1E-8;
134const int dx4[4]={1,0,0,-1};
135const int dy4[4]={0,-1,1,0};
136const int inf = 0x3f3f3f3f;
137#define MAXALP 30
138const int N=5E5+7;
139struct SAM
140{
141 struct state
142 {
143 int len, link, nxt[MAXALP];
144 int leftmost; //某个状态的right集合中r值最小的
145 int rightmost;//某个状态的right集合的r的最大值
146 int Right; //right集合大小
147 };
148 state st[N*2];
149 char S[N];
150 int sz, last,rt;
151 char s[N];
152 int cnt[2*N],rk[2*N];//for radix sort
153 void init()
154 {
155 sz = 0;
156 last = rt = ++sz;
157 st[1].len = 0;
158 st[1].link=-1;
159 st[1].rightmost=0;
160 ms(st[1].nxt,-1);
161 }
162 void extend(int c,int head)
163 {
164 int cur = ++sz;
165 st[cur].len = st[last].len + 1;
166 st[cur].leftmost = st[cur].rightmost = head;
167 memset(st[cur].nxt, -1, sizeof(st[cur].nxt));
168 int p;
169 for (p = last; p != -1 && st[p].nxt[c] == -1; p = st[p].link)
170 st[p].nxt[c] = cur;
171 if (p == -1) {
172 st[cur].link = rt;
173 } else {
174 int q = st[p].nxt[c];
175 if (st[p].len + 1 == st[q].len) {
176
177 st[cur].link = q;
178 } else {
179 int clone = ++sz ;
180 st[clone].len = st[p].len + 1;
181 st[clone].link = st[q].link;
182 memcpy(st[clone].nxt, st[q].nxt, sizeof(st[q].nxt));
183 st[clone].leftmost = st[q].leftmost;
184 st[clone].rightmost = st[q].rightmost;
185 for (; p != -1 && st[p].nxt[c] == q; p = st[p].link)
186 st[p].nxt[c] = clone;
187 st[q].link = st[cur].link = clone;
188 }
189 }
190 last = cur;
191 }
192 void topo()
193 {
194 ms(cnt,0);
195 for (int i = 1 ; i <= sz ; i++) cnt[st[i].len]++;
196 for ( int i = 1 ; i <= sz ; i++) cnt[i]+=cnt[i-1];
197 for (int i = 1 ; i <= sz ;i++) rk[cnt[st[i].len]--] = i;
198 }
199 void pre() //跑拓扑序,预处理一些东西
200 {
201 for ( int i = sz ; i >= 2 ; i--)
202 {
203 int v = rk[i];
204 int fa = st[v].link;
205 if (fa==-1) continue;
206 st[fa].rightmost = max(st[fa].rightmost,st[v].rightmost);
207 st[fa].Right += st[v].Right;
208 }
209 }
210 void solve()
211 {
212
213 LL ans = 0 ;
214 for ( int i = 1 ; i <= sz ; i++) if (st[i].link!=-1&&st[i].rightmost-st[i].leftmost>st[st[i].link].len)
215 ans += min(st[i].len,st[i].rightmost-st[i].leftmost)-st[st[i].link].len;
216 printf("%lld\n",ans);
217 }
218 int LCS(char *s)
219 {
220 int ans = 0,len = 0 ;
221 int p = rt;
222 for ( int i = 0 ,_len = strlen(s) ; i < _len ; i++)
223 {
224 int ID = s[i]-'a';
225 if (st[p].nxt[ID]!=-1) p = st[p].nxt[ID],len++;
226 else
227 {
228 while (p!=-1&&st[p].nxt[ID]==-1) p = st[p].link;
229 if (p==-1) p=rt,len=0;else len = st[p].len+1,p = st[p].nxt[ID];
230 }
231 // printf("len:%d\n",len);
232 ans = max(ans,len);
233 }
234 return ans;
235 }
236}A;
237char B[N];
238int main()
239{
240#ifndef ONLINE_JUDGE
241 freopen("./in.txt","r",stdin);
242#endif
243 A.init();
244 cin>>A.S>>B;
245 for ( int i = 0 ,len=strlen(A.S) ; i < len ; i++) A.extend(A.S[i]-'a',i);
246 int ans = A.LCS(B);
247 cout<<ans<<endl;
248#ifndef ONLINE_JUDGE
249 fclose(stdin);
250#endif
251 return 0;
252}
