SP1812 LCS2 - Longest Common Substring II
还是求几个串的最长公共串。
但是由于数据范围的原因,用后缀数组可能会T,所以现在介绍一种后缀自动机的解法。
首先考虑两个串的公共串怎么求(假设分别为 \(S\)、\(T\))。对 \(S\) 建立后缀自动机,把 \(T\) 放在后缀自动机上跑,如果没有转移就跳后缀 \(link\)。然后在每个节点记录一个最大值最后取个 \(\max\) 就行。
现在你有很多串。对第一个串建SAM,然后其余每个串在SAM上跑一遍,还是每个节点存一下公共串的最大值,注意 \(parent\) 树上的每个子孙对祖先都有贡献,然后就做完了。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int INF=1<<30,N=3000000;
char s[N];
int ans[N];
struct Suffix_DFA
{
int head[N],cnt,Siz,last,Max[N];
struct SAM
{
int link,ch[30],len;
}sam[N];
struct Edge
{
int nxt,to;
}g[N*2];
void add(int from,int to)
{
g[++cnt].nxt=head[from];
g[cnt].to=to;
head[from]=cnt;
}
void SAM_Extend(int k)
{
int cur=++Siz;
sam[cur].len=sam[last].len+1;
int p=last;
while(p!=-1&&!sam[p].ch[k])
sam[p].ch[k]=cur,
p=sam[p].link;
if(p==-1)
sam[cur].link=0;
else
{
int q=sam[p].ch[k];
if(sam[q].len==sam[p].len+1)
sam[cur].link=q;
else
{
int clone=++Siz;
sam[clone].len=sam[p].len+1;
sam[clone].link=sam[q].link;
for (int i=0;i<26;i++)
sam[clone].ch[i]=sam[q].ch[i];
while(p!=-1&&sam[p].ch[k]==q)
sam[p].ch[k]=clone,
p=sam[p].link;
sam[cur].link=sam[q].link=clone;
}
}
last=cur;
}
void build()
{
for (int i=1;i<=Siz;i++)
add(sam[i].link,i);
}
void DFS(int x)
{
for (int i=head[x];i;i=g[i].nxt)
{
int v=g[i].to;
DFS(v);
Max[x]=min(sam[x].len,max(Max[x],Max[v]));
}
ans[x]=min(ans[x],Max[x]);
}
void work(char s[],int len)
{
memset(Max,0,sizeof(Max));
int l=0,k=0;
for (int i=1;i<=len;i++)
{
int t=s[i]-'a';
while(k&&!sam[k].ch[t])
k=sam[k].link,
l=sam[k].len;
if(sam[k].ch[t])
l++,k=sam[k].ch[t];
Max[k]=max(Max[k],l);
}
DFS(0);
}
}S;
void work()
{
S.sam[0].link=-1;
scanf("%s",s+1);
int len=strlen(s+1);
for (int i=1;i<=len;i++)
S.SAM_Extend(s[i]-'a');
S.build();
for (int i=1;i<=S.Siz;i++)
ans[i]=INF;
while(~scanf("%s",s+1))
{
len=strlen(s+1);
S.work(s,len);
}
int Ans=0;
for (int i=1;i<=S.Siz;i++)
Ans=max(Ans,ans[i]);
printf("%d\n",Ans);
}
int main()
{
work();
return 0;
}
由于博主比较菜,所以有很多东西待学习,大部分文章会持续更新,另外如果有出错或者不周之处,欢迎大家在评论中指出!