SP1812-LCS2-后缀自动机
题目描述
题解
对于一个串构建
S
A
M
SAM
SAM
每个串依次进行匹配
同时记录
t
r
[
i
]
.
m
a
x
n
tr[i].maxn
tr[i].maxn表示走到了
i
i
i节点能够匹配上的最长公共子串的长度
当然,每个串的tr[i].maxn可以更新
t
r
[
t
r
[
i
]
.
f
a
]
.
m
a
x
n
tr[tr[i].fa].maxn
tr[tr[i].fa].maxn
所以需要拓扑排序
对于每个串求出每个节点的最长匹配
然后对他们取
m
i
n
min
min,表示某个节点大家都能匹配的最长长度
最后对于所有点的值都取个
m
a
x
max
max就是答案
代码
#include<bits/stdc++.h>
#define M 100009
using namespace std;
struct sam{
int fa,len,minx,maxn,ch[26];
}tr[M*4];
int ans,last,cnt,rt,vis[M*4],id[M*4];//后缀自动机相关的数组要开2倍以上
char s[M];
void extend(int pos){//构造后缀自动机
int np=++cnt,p=last,val=s[pos]-'a';
last=np,tr[np].len=pos;
while(p&&!tr[p].ch[val]) tr[p].ch[val]=np,p=tr[p].fa;
if(p==0) tr[np].fa=rt;
else{
int q=tr[p].ch[val];
if(tr[q].len==tr[p].len+1) tr[np].fa=q;
else{
int nq=++cnt;
tr[nq].len=tr[p].len+1;
memcpy(tr[nq].ch,tr[q].ch,sizeof(tr[q].ch));
tr[nq].fa=tr[q].fa,tr[np].fa=tr[q].fa=nq;
while(p&&tr[p].ch[val]==q) tr[p].ch[val]=nq,p=tr[p].fa;
}
}
}
int getans(){
int n=strlen(s+1),p=rt,len=0;
for(int i=1;i<=n;i++){//此处与两个串的匹配方式相同
int val=s[i]-'a';
if(tr[p].ch[val]) len++,p=tr[p].ch[val];
else{
while(p&&!tr[p].ch[val]) p=tr[p].fa;
if(p==0) len=0,p=rt;
else len=tr[p].len+1,p=tr[p].ch[val];
}tr[p].maxn=max(tr[p].maxn,len);
}
for(int i=cnt;i>=1;i--){//由拓扑序更新
int v=id[i];
tr[v].minx=min(tr[v].minx,tr[v].maxn);
if(tr[v].fa&&tr[v].maxn>=tr[v].len)
tr[tr[v].fa].maxn=tr[tr[v].fa].len;
}
}
int main(){
scanf("%s",s+1);
int n=strlen(s+1);last=rt=++cnt;
for(int i=1;i<=n;i++) extend(i);
for(int i=1;i<=cnt;i++) vis[tr[i].len]++;
for(int i=1;i<=cnt;i++) vis[i]+=vis[i-1];
for(int i=1;i<=cnt;i++) id[vis[tr[i].len]--]=i;//O(n)的拓扑排序
for(int i=1;i<=cnt;i++) tr[i].minx=tr[i].len;//赋初始值
while(~scanf("%s",s+1)){
for(int i=1;i<=cnt;i++) tr[i].maxn=0;//记得清空maxn
getans();
}for(int i=1;i<=cnt;i++) ans=max(ans,tr[i].minx);
printf("%d\n",ans);
return 0;
}