Description
给出几个由小写字母构成的单词,求它们最长的公共子串的长度。
任务:
l
读入单词
l
计算最长公共子串的长度
l
输出结果
Input
文件的第一行是整数
n
,
1<=n<=5
,表示单词的数量。接下来
n
行每行一个单词,只由小写字母组成,单词的长度至少为
1
,最大为
2000
。
Output
仅一行,一个整数,最长公共子串的长度。
Sample Input
3
abcb
bca
acbc
abcb
bca
acbc
Sample Output
2
首先一点,root到一个结点的pre包含这个节点的所有后缀。
那么就很好做了!
对第一个串建立后缀自动机。然后跑匹配
到每个节点的len的最大值取最小值,然后再把所有最小值求最大值就是答案了
初始每个节点的ans[i]为step[i],为了防止路径超过根到当前点的最长路径
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
using namespace std;
struct sam
{
int son[1000001][26],pre[1000001],step[1000001];
int ans[1000001],v[1000001],q[1000001];
int len[1000001];
int last,tot;
sam()
{
last=1;
tot=1;
}
inline void push_back(int v)
{
tot++;
step[tot]=v;
}
inline void prep()
{
int i;
for(i=1;i<=tot;i++)
ans[i]=step[i];
for(i=1;i<=tot;i++)
v[step[i]]++;
for(i=1;i<=tot;i++)
v[i]+=v[i-1];
for(i=tot;i>=1;i--)
q[v[step[i]]--]=i;
}//基数排序,确定树的遍历
inline void extend(int x)
{
push_back(step[last]+1);
int p=last,np=tot;
while(son[p][x]==0&&p!=0)
{
son[p][x]=np;
p=pre[p];
}
if(p==0)
pre[np]=1;
else
{
int q=son[p][x];
if(step[q]!=step[p]+1)
{
push_back(step[p]+1);
int nq=tot;
//memcpy(son[nq],son[q],sizeof(son[q]));
int i;
for(i=0;i<=25;i++)
son[nq][i]=son[q][i];
pre[nq]=pre[q];
pre[q]=pre[np]=nq;
while(son[p][x]==q)
{
son[p][x]=nq;
p=pre[p];
}
}
else
pre[np]=q;
}
last=np;
}
inline void build()
{
string x;
cin>>x;
int lx=x.size();
int i;
for(i=0;i<=lx-1;i++)
extend(x[i]-'a');
}
inline void solve()
{
memset(len,0,sizeof(len));
string x;
cin>>x;
int lx=x.size();
int p=1,tmp=0;
int i;
for(i=0;i<=lx-1;i++)
{
int xt=x[i]-'a';
while(son[p][xt]==0&&p!=0)
p=pre[p];
if(p==0)
{
p=1;
tmp=0;
}
else
{
tmp=min(tmp,step[p])+1;
p=son[p][xt];
}
len[p]=max(len[p],tmp);
}
for(i=tot;i>=1;i--)
len[pre[q[i]]]=max(len[pre[q[i]]],len[q[i]]);
for(i=1;i<=tot;i++)
ans[i]=min(ans[i],len[i]);
}
}sam;
int main()
{
int n;
scanf("%d",&n);
sam.build();
int i;
sam.prep();
for(i=2;i<=n;i++)
sam.solve();
int ans=0;
for(i=1;i<=sam.tot;i++)
ans=max(ans,sam.ans[i]);
printf("%d\n",ans);
return 0;
}