2018.12.15【SPOJ-LCS2】Longest Common Substring II(字尾自動機SAM)
阿新 • • 發佈:2018-12-17
傳送門
解析:
這道題可以把所有串接在一起構建字尾自動機來做,但是那樣還不如寫字尾陣列。。。
所以這裡提供一個只有字尾自動機能實現的做法。
思路:
首先構建出第一個串的字尾自動機。
然後拿其他的串放到字尾自動機上面跑。同時更新答案。
程式碼裡面的 記錄的是該串在該狀態下能夠與前面所有串產生的最大交集, 記錄的是前面所有串在該狀態下能夠產生的最大交集。
跑的時候記錄一下目前匹配了的最長長度,根據下一個狀態分別處理。
1.當前狀態已經具有字元
的轉移
直接進入下一個狀態,當前匹配長度+1,更新下一個狀態的交集大小。
接下來的過程有點像構建字尾自動機時候的做法。
2.否則,我們向上跳fail,直到徹底失配或者遇到一個具有字元
轉移的狀態。
(1)如果徹底失配,清空當前匹配長度,
(2)否則,匹配長度為當前狀態
,因為我們馬上就有下一個
能夠多匹配一位。進入下一個狀態。
一次狀態更新完成後,我們都需要對它fail鏈上面的狀態全部更新,因為他們全部能夠完成匹配。其實就是所有後綴的狀態更新一遍。
程式碼:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
typedef struct SAM_node *point;
struct SAM_node{
int len,l;
point fa,son[26];
SAM_node():fa(NULL),len(0){}
};
cs int N=100005;
struct SAM{
SAM_node nd[N<<1];
point now,last;
SAM():now(nd),last(nd){}
inline void push_back(char c){
c-='a';
point cur=++now;
cur->len=last->len+1;
point p=last;
for(;p&&!p->son[c];p=p->fa)p->son[c]=cur;
if(!p)cur->fa=nd;
else if(p->son[c]->len==p->len+1)cur->fa=p->son[c];
else {
point clone=++now,q=p->son[c];
*clone=*q;
clone->len=p->len+1;
q->fa=cur->fa=clone;
for(;p&&p->son[c]==q;p=p->fa)p->son[c]=clone;
}
last=cur;
}
inline void make_LCS(char *s,int len){
re point u=nd;re int nowl=0;
for(int re i=1;i<=len;++i){
re int c=s[i]-'a';
if(u->son[c]){
u=u->son[c];
u->l=max(u->l,min(u->len,++nowl));
}
else{
for(;u&&!u->son[c];u=u->fa);
if(!u)u=nd,nowl=0;
else {
nowl=u->len+1;
u=u->son[c];
u->l=max(u->l,min(u->len,nowl));
}
}
for(point re p=u->fa;p&&p->l!=p->len;p=p->fa)p->l=p->len;
}
for(u=nd;u<=now;++u)u->len=u->l,u->l=0;
}
inline int query(){
int ans=0;
for(point re p=nd;p<=now;++p)ans=max(ans,p->len);
return ans;
}
}sam;
char s[N];int len;
signed main(){
scanf("%s",s+1);len=strlen(s+1);
for(int re i=1;i<=len;++i)sam.push_back(s[i]);
while(~scanf("%s",s+1)){
len=strlen(s+1);
sam.make_LCS(s,len);
}
printf("%d",sam.query());
return 0;
}