正题
题目链接:https://www.luogu.com.cn/problem/SP1811
题目大意
求两个串的最长公共子串。
解题思路
对与第一个串首先构建一个 S A M SAM SAM,然后考虑让第二个串在上面匹配。
对于枚举的每个位置要在 S A M SAM SAM上找一个节点使得它的后缀是枚举到的位置的后缀。所以对于当前节点如果有新加入的字符串的边就走过去,如果没有我们就可以在 p a r e n t parent parent树上往前跳,因为跳到的节点代表的后缀是满足且仅满足这些位置的后缀是加入这个字符之前的后缀,跳了之后取那个节点的 l e n len len就好了。
时间复杂度 O ( n ) O(n) O(n)
c o d e code code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5e5+10;
int n,las,tot,len[N],fa[N];
char s[N];int ch[N][26],ans;
void add(int c){
int p=las;int np=las=++tot;
len[np]=len[p]+1;
for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
if(!p)fa[np]=1;
else{
int q=ch[p][c];
if(len[p]+1==len[q])fa[np]=q;
else{
int nq=++tot;len[nq]=len[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q];fa[q]=fa[np]=nq;
for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq;
}
}
return;
}
int main()
{
las=tot=1;
scanf("%s",s+1);n=strlen(s+1);
for(int i=1;i<=n;i++)
add(s[i]-'a');
memset(s,0,sizeof(s));
scanf("%s",s+1);n=strlen(s+1);
int now=1,L=0;
for(int i=1;i<=n;i++){
int c=s[i]-'a';
if(ch[now][c])now=ch[now][c],++L;
else{
for(;now&&!ch[now][c];now=fa[now]);
if(now)L=len[now]+1,now=ch[now][c];
else L=0,now=1;
}
ans=max(ans,L);
}
printf("%d",ans);
}