【题目描述】
给定两个字符串A和B,求它们的最长公共子串
【分析】
我们考虑将A串建成后缀自动机
令当前状态为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。
在过程中更新状态的最大匹配长度
注意到我们求的是对于任意一个Right集合中的r,最大的匹配长度。那么对于一个状态s,它的结果自然也可以作为它Parent的结果,我们可以从底到上更新一遍。
然后问题就解决了。
———来自陈立杰《后缀自动机讲稿》
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
using namespace std;
#define FILE "read"
#define MAXN 200010
#define up(i,j,n) for(int i=j;i<=n;++i)
#define dn(i,j,n) for(int i=j;i>=n;--i)
#define cmax(a,b) a=max(a,b)
#define cmin(a,b) a=min(a,b)
char ch[MAXN];
int n,cnt(1),now(1),len,ans,mx[MAXN],par[MAXN],son[MAXN][27];
void insert(int x){
int p=now,np=++cnt;
mx[np]=mx[now]+1; now=np;
while(p&&!son[p][x]) son[p][x]=np,p=par[p];
if(!p) par[np]=1;
else{
int q=son[p][x];
if(mx[q]==mx[p]+1) par[np]=q;
else{
int nq=++cnt;
mx[nq]=mx[p]+1;
memcpy(son[nq],son[q],sizeof(son[q]));
par[nq]=par[q];
par[q]=par[np]=nq;
while(p&&son[p][x]==q)son[p][x]=nq,p=par[p];
}
}
}
int walk(int x){
while(!son[now][x]&&par[now]){
now=par[now];
len=mx[now];
}
if(!son[now][x]) return 0;
now=son[now][x]; len++;
return len;
}
int main(){
freopen(FILE".in","r",stdin);
freopen(FILE".out","w",stdout);
scanf("%s",ch+1); n=strlen(ch+1);
up(i,1,n) insert(ch[i]-'a');
scanf("%s",ch+1); n=strlen(ch+1); now=1;
up(i,1,n) cmax(ans,walk(ch[i]-'a'));
printf("%d\n",ans);
return 0;
}