给出几个由小写字母构成的单词,求它们最长的公共子串的长度。
任务:
l 读入单词
l 计算最长公共子串的长度
l 输出结果
可以把广义后缀自动机造出来然后每个前缀的结束点往上暴力跳,直到当前点被当前串覆盖过为止。如果当前串长度为
l
l
l,自动机大小为
n
n
n,那么复杂度是
m
i
n
(
l
2
,
n
)
min(l^2,n)
min(l2,n)
平均每个字符的复杂度贡献为
m
i
n
(
l
2
,
n
)
l
<
=
n
\frac{min(l^2,n)}l <= \sqrt n
lmin(l2,n)<=n(这个证明和在线AC自动机的复杂度证明是一致的)。
那么复杂度上限就是
O
(
n
∑
l
)
O(\sqrt n \sum l)
O(n∑l)
注意在边插入边修改的情况下复制节点复制的信息要全。
AC Code:
#include<bits/stdc++.h>
#define maxn 20005
#define maxc 26
using namespace std;
int tr[maxn][maxc],fail[maxn],len[maxn];
int tot;
char s[2005];
int vis[maxn],cnt[maxn];
void ins(int p,int c){
if(tr[p][c]){
int q=tr[p][c];
if(len[q]==len[p]+1) return;
else{
int cln = ++tot;
memcpy(tr[cln],tr[q],sizeof tr[q]),cnt[cln]=cnt[q],
vis[cln]=vis[q],fail[cln]=fail[q],len[cln]=len[p]+1;
for(;p!=-1&&tr[p][c]==q;p=fail[p]) tr[p][c]=cln;
fail[q]=cln;
}
}
else{
int cur=++tot,q;
len[cur] = len[p] + 1;
for(;p!=-1 && !tr[p][c];p=fail[p]) tr[p][c]=cur;
if(p==-1) fail[cur]=0;
else if(len[q=tr[p][c]]==len[p]+1) fail[cur]=q;
else{ int cln=++tot;
memcpy(tr[cln],tr[q],sizeof tr[q]),cnt[cln]=cnt[q],
vis[cln]=vis[q],fail[cln]=fail[q],len[cln]=len[p]+1;
for(;p!=-1&&tr[p][c]==q;p=fail[p]) tr[p][c]=cln;
fail[q]=fail[cur]=cln;
}
}
}
int main(){
int n;
scanf("%d",&n);
fail[0]=-1;
for(int i=1;i<=n;i++){
scanf("%s",s);
int m=strlen(s),p=0;
for(int j=0;j<m;j++){
ins(p,s[j]-'a');
p=tr[p][s[j]-'a'];
for(int k=p;k!=-1&&vis[k]<i;k=fail[k])
vis[k]=i,cnt[k]++;
}
}
int ans = 0;
for(int i=1;i<=tot;i++)
if(cnt[i]==n)
ans = max(ans , len[i]);
printf("%d\n",ans);
}