bzoj2946 [Poi2000]公共串 后缀自动机

本文介绍了一种使用后缀自动机(SAM)寻找多个字符串间的最长公共子串的方法。通过逐步构建SAM并更新状态来高效地解决问题。文章提供了完整的C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原来2000年就有SAM了,害怕
先对第一个串建一个SAM,然后把其他串在上面匹配,得出每个状态的最大匹配长度,然后对每个状态取所有串的最小值,这就是最长公共子串,然后对所有子串求个mx就好了。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e5+5;
int ch[N][30],fa[N],mx[N],ans[N],l[N];
int b[N],c[N]; 
char s[N];
int n,m,cnt,last;
inline void ins(int x)
{
    int p,q,np,nq;
    p=last,last=np=++cnt;
    l[np]=l[p]+1;
    for(;!ch[p][x]&&p;p=fa[p])ch[p][x]=np;
    if (!p)fa[np]=1;
    else
    {
        q=ch[p][x];
        if (l[q]==l[p]+1)fa[np]=q;
        else
        {
            nq=++cnt;
            l[nq]=l[p]+1;
            memcpy(ch[nq],ch[q],sizeof(ch[q]));
            fa[nq]=fa[q];
            fa[q]=fa[np]=nq;
            for(;ch[p][x]==q;p=fa[p])ch[p][x]=nq;
        }
    }
}
inline void pre()
{
    fo(i,1,cnt)b[l[i]]++;
    fo(i,1,n)b[i]+=b[i-1];
    fo(i,1,cnt)c[b[l[i]]--]=i;
    fo(i,1,cnt)ans[i]=l[i];
}
inline void solve()
{
    memset(mx,0,sizeof(mx));
    int x=1,len=0;
    fo(i,1,n)
    {
        s[i]-='a';
        for(;!ch[x][s[i]]&&x;x=fa[x]);
        if (!x)x=1,len=0;
        else len=min(len,l[x])+1,x=ch[x][s[i]];
        mx[x]=max(mx[x],len);
    }
    fd(i,cnt,1)mx[fa[c[i]]]=max(mx[fa[c[i]]],mx[c[i]]);
    fo(i,1,cnt)ans[i]=min(ans[i],mx[i]);
}
int main()
{
    scanf("%d",&m);
    scanf("%s",s+1);
    n=strlen(s+1);
    last=cnt=1;
    fo(i,1,n)ins(s[i]-'a');
    pre();
    fo(i,1,m-1)
    {
        scanf("%s",s+1);
        n=strlen(s+1);
        solve();
    }
    int ans1=0;
    fo(i,1,cnt)ans1=max(ans1,ans[i]);
    printf("%d\n",ans1);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值