bzoj2946 [Poi2000]公共串

本文介绍了一种求解多个字符串最长公共子串的问题,并提供了一个详细的算法实现过程。通过SAM(Suffix Array Mania)自动机的方法,文章展示了如何处理输入的字符串集合,计算并输出这些字符串间的最长公共子串的长度。

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

http://www.elijahqi.win/archives/2855
Description

   给出几个由小写字母构成的单词,求它们最长的公共子串的长度。

任务:
l 读入单词
l 计算最长公共子串的长度
l 输出结果

Input

文件的第一行是整数 n,1<=n<=5,表示单词的数量。接下来n行每行一个单词,只由小写字母组成,单词的长度至少为1,最大为2000。

Output

仅一行,一个整数,最长公共子串的长度。

Sample Input

3
abcb
bca
acbc
Sample Output


求多个串的最长公共子串 首先针对第一个串建立sam 设mx[x]表示所有串在这个节点匹配的最长距离是多少 tmp[x]表示当前这个串和第一个串在该节点的最长匹配是多少
因为一次匹配不可能更新所有的tmp值所以我们需要在匹配之后拓扑排序之后 因为父节点肯定是当前节点的后缀所以匹配长度不会减少 然后每次更新完tmp之后再去更新mx
len[fa[p]]

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100010
using namespace std;
char s[N];int n,len[N<<1],fa[N<<1],cnt=1,root=1,last=1;
int c[N],tmp[N<<1],mx[N<<1],ans,ch[N<<1][26],rk[N<<1];
inline void insert1(int x){
    int np=++cnt,p=last;len[np]=len[p]+1;
    for (;p&&!ch[p][x];p=fa[p]) ch[p][x]=np;
    if (!p) fa[np]=root;else{
        int q=ch[p][x];if (len[p]+1==len[q]) fa[np]=q;else{
            int nq=++cnt;fa[nq]=fa[q];memcpy(ch[nq],ch[q],sizeof(ch[q]));
            fa[q]=fa[np]=nq;len[nq]=len[p]+1;for (;p&&ch[p][x]==q;p=fa[p]) ch[p][x]=nq;
        }
    }last=np;
}
int main(){
    freopen("spoj1812.in","r",stdin);
    int T;scanf("%d",&T);
    scanf("%s",s+1);n=strlen(s+1);
    for (int i=1;i<=n;++i) insert1(s[i]-'a');
    for (int i=1;i<=cnt;++i) ++c[len[i]];
    for (int i=1;i<=n;++i) c[i]+=c[i-1];memset(mx,0x3f,sizeof(mx));
    for (int i=cnt;i;--i) rk[c[len[i]]--]=i;
    while(~scanf("%s",s+1)){int m=strlen(s+1);
        memset(tmp,0,sizeof(tmp));int now=1,l=0;
        for (int i=1;i<=m;++i){
            if (ch[now][s[i]-'a']) ++l,now=ch[now][s[i]-'a'];
            else{
                while(now&&!ch[now][s[i]-'a']) now=fa[now];
                if (!now) now=1,l=0;else l=len[now]+1,now=ch[now][s[i]-'a'];
            }tmp[now]=max(tmp[now],l);
        }
        for (int i=cnt;i;--i){
            int x=rk[i];mx[x]=min(mx[x],tmp[x]);
            if(fa[x]&&tmp[x]) tmp[fa[x]]=len[fa[x]];
        }
    }for (int i=1;i<=cnt;++i) ans=max(ans,mx[i]);printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值