题目链接
链接是洛谷有翻译的。
题意:
给定一些字符串,求出它们的最长公共子串。输入至多10行,每行包含不超过100000个的小写字母。
这题似乎和POI2000BZOJ2946是同一道题,于是我就也挂上那个标签了。PS:BZOJ的那个是个权限题。。。
题解:
我们之前做过两个串通过SAM找最长公共子串的,具体请看这里。
那么我们考虑多个串的做法,其实多个串是可以从两个串拓展来的。思路上就是我们对第一个串建出SAM,然后其他串与这个串匹配,我们对于当前串,算出每个位置能匹配的最大值,然后对于所有的串,我们要对于每个位置取min,因为最小的才能是公共的。具体的实现的话就是每次拿出一个串,像两个串那样在SAM上跑,跑完之后根据parent树更新一下parent树上的点的答案。然后再对每个位置取min。写的时候对于一个字符串,处理它的时候维护一个每个位置的max,对于所有的串,维护一个每个位置的min。最后答案是所有的min中的max。说起来好像挺绕的,但是其实体会一下还是不难理解的。
代码:
#include <bits/stdc++.h>
using namespace std;
int n,fa[400010],len[400010],rt=1,cnt=1,lst=1,ch[400010][26];
int hed[400010],num,ans,mn[400010],mx[400010];
struct node
{
int to,next;
}a[800010];
char s[100010];
inline void insert(int x)
{
int cur=++cnt,pre=lst;
lst=cur;
len[cur]=len[pre]+1;
for(;pre&&!ch[pre][x];pre=fa[pre])
ch[pre][x]=cur;
if(!pre)
fa[cur]=rt;
else
{
int ji=ch[pre][x];
if(len[ji]==len[pre]+1)
fa[cur]=ji;
else
{
int gg=++cnt;
memcpy(ch[gg],ch[ji],sizeof(ch[ji]));
fa[gg]=fa[ji];
fa[ji]=fa[cur]=gg;
len[gg]=len[pre]+1;
for(;pre&&ch[pre][x]==ji;pre=fa[pre])
ch[pre][x]=gg;
}
}
}
inline void add(int from,int to)
{
a[++num].to=to;
a[num].next=hed[from];
hed[from]=num;
}
inline void dfs(int x)
{
for(int i=hed[x];i;i=a[i].next)
{
int y=a[i].to;
dfs(y);
mx[x]=max(mx[x],min(mx[y],len[x]));
}
}
inline void qwq()
{
int l=0,cur=rt;
memset(mx,0,sizeof(mx));
for(int i=1;i<=n;++i)
{
int x=s[i]-'a';
if(ch[cur][x])
{
cur=ch[cur][x];
++l;
}
else
{
for(;cur&&!ch[cur][x];cur=fa[cur]);
if(!cur)
{
cur=rt;
l=0;
}
else
{
l=len[cur]+1;
cur=ch[cur][x];
}
}
mx[cur]=max(mx[cur],l);
}
dfs(1);
for(int i=1;i<=cnt;++i)
mn[i]=min(mn[i],mx[i]);
}
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
for(int i=1;i<=n;++i)
insert(s[i]-'a');
for(int i=2;i<=cnt;++i)
add(fa[i],i);
memset(mn,0x3f,sizeof(mn));
while(~scanf("%s",s+1))
{
n=strlen(s+1);
qwq();
}
for(int i=1;i<=cnt;++i)
ans=max(ans,mn[i]);
printf("%d\n",ans);
return 0;
}