bzoj 2946: [Poi2000]公共串 后缀自动机

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33229466/article/details/56678841

题意

给出几个由小写字母构成的单词,求它们最长的公共子串的长度。
任务:
l 读入单词
l 计算最长公共子串的长度
l 输出结果
1<=n<=5,表示单词的数量,单词只由小写字母组成,单词的长度至少为1,最大为2000。

分析

先对第一个字符串构建sam,然后对于其余每个串,记录每个位置在后缀自动机上匹配到的最大长度,然后有一个特别关键的转移就是每个儿子节点的mx传给其parents,再在所有位置里面找一个最大的即可。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define N 4005
using namespace std;

int n,mx[N],ans[N],fa[N],l[N],ch[N][30],last,cnt,b[N],c[N],m;
char s[N];

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;
        }
    }
}

void solve()
{
    memset(mx,0,sizeof(mx));
    int now=1,len=0;
    for (int i=1;i<=n;i++)
    {
        s[i]-='a';
        for (;!ch[now][s[i]]&&now;now=fa[now]);
        if (!now) now=1,len=0;
        else len=min(len,l[now])+1,now=ch[now][s[i]];
        mx[now]=max(mx[now],len);
    }
    for (int i=cnt;i>=1;i--) mx[fa[c[i]]]=max(mx[fa[c[i]]],mx[c[i]]);
    for (int i=1;i<=cnt;i++) ans[i]=min(ans[i],mx[i]);
}

void prework()
{
    for (int i=1;i<=cnt;i++) b[l[i]]++;
    for (int i=1;i<=n;i++) b[i]+=b[i-1];
    for (int i=1;i<=cnt;i++) c[b[l[i]]--]=i;
    for (int i=1;i<=cnt;i++) ans[i]=l[i];
}

int main()
{
    scanf("%d",&m);
    scanf("%s",s+1);
    n=strlen(s+1);
    last=cnt=1;
    for (int i=1;i<=n;i++) ins(s[i]-'a');
    prework();
    for (int i=1;i<m;i++)
    {
        scanf("%s",s+1);
        n=strlen(s+1);
        solve();
    }
    int w=0;
    for (int i=1;i<=cnt;i++) w=max(w,ans[i]);
    printf("%d",w);
    return 0;
}
阅读更多
换一批

没有更多推荐了,返回首页