[SPOJ1812]LCS2 - Longest Common Substring II-后缀自动机

本文介绍了一种寻找多个字符串中最长公共子串的算法。利用后缀自动机进行构建,并通过基数排序确保正确的拓扑顺序,实现高效求解。

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

LCS2 - Longest Common Substring II

Description

A string is finite sequence of characters over a non-empty finite set Σ.

In this problem, Σ is the set of lowercase letters.

Substring, also called factor, is a consecutive sequence of characters occurrences at least once in a string.

Now your task is a bit harder, for some given strings, find the length of the longest common substring of them.

Here common substring means a substring of two or more strings.

Input

The input contains at most 10 lines, each line consists of no more than 100000 lowercase letters, representing a string.

Output

The length of the longest common substring. If such string doesn’t exist, print “0” instead.

Example

Input:

alsdfkjfjkdsal
fdjskalajfkdsla
aaaajfaaaa

Output:

2

Notice: new testcases added

//不开心.jpg
┏┻┻┻┻┻┻┻┓
┃ ┏┓   ┏┓ ┃
┃ ┗┛ ^ ┗┛ ┃
┗┏━┓━┏━┓┛
  ┗━┛  ┗━┛
     |   |

//spoj有毒.jpg

spoj还我青春!!!
在此向所有做这道题的人一个提醒:样例错了!!!
样例输出是3!!!不是2!!!
调了我一晚上啊啊啊

(╯‵□′)╯︵┻━┻

发泄完了回归正文:
思路:
先对第一个串建后缀自动机~
然后跟1811一样的思路,但由于是多个串,所以需要我们维护每个节点能匹配的最短匹配值,最后取其最大值作为答案。

具体方法是在每次匹配时开一个数组记录当前匹配过程中当前节点匹配到的最长匹配值,在每次结束匹配后用它的值更新最短匹配值即可。

需要注意的是,在更新时,需要对每个经过的节点,更新它们的父亲为其len值并传递下去,因为根据后缀自动机的性质,经过了当前节点,其父亲相当于也被经过了一次,只是在匹配中实际走到的是其等价的后缀节点而不是这个父亲节点。

问题来了:怎么保证刚好传递完所有的理应被经过的点?
其实,把所有节点根据长度做一遍基数排序,按顺序更新就可以保证不会出错,跑出来的更新顺序一定是拓扑序,刚好满足传递完所有的要传递的点。
为什么呢?
因为建自动机时拓扑序在后的节点len值更大啊~
所以就没有然后了~

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>

using namespace std;

const int N=200233;

struct SAM
{
    int next[N][26],fa[N],len[N];
    int pool,u,id[N],b[N];
    int mn[N],mx[N];

    inline void init()
    {
        pool=u=1;
        memset(next,0,sizeof(next));
        memset(len,0,sizeof(len));
        memset(mn,127,sizeof(mn));
        memset(fa,0,sizeof(fa));
        memset(id,0,sizeof(id));
    }

    inline void add(int v)
    {
        int now=++pool;
        len[now]=len[u]+1;
        while(!next[u][v] && u)
            next[u][v]=now,u=fa[u];

        if(!u)
            fa[now]=1;
        else
        {
            int q=next[u][v];
            if(len[q]==len[u]+1)
                fa[now]=q;
            else
            {
                int newq=++pool;
                len[newq]=len[u]+1;
                fa[newq]=fa[q];
                memcpy(next[newq],next[q],sizeof(next[q]));

                fa[q]=fa[now]=newq;

                while(next[u][v]==q)
                    next[u][v]=newq,u=fa[u];
            }
        }
        u=now;
    }

    inline void topsort(int lenn)
    {
        for(int i=1;i<=pool;i++)
            ++b[len[i]];
        for(int i=1;i<=lenn;i++)
            b[i]+=b[i-1];
        for(int i=1;i<=pool;i++)
            id[b[len[i]]--]=i;
    }

    inline void run(char *s)
    {
        int now=1;
        int tmp=0;
        int lenn=strlen(s+1);

        memset(mx,0,sizeof(mx));
        for(int i=1;i<=lenn;i++)
        {
            while(!next[now][s[i]-'a'] && now)
                now=fa[now],tmp=len[now];
            if(!now)
                now=1,tmp=0;
            else
                tmp++,now=next[now][s[i]-'a'];

            mx[now]=max(mx[now],tmp);
        }

        for(int i=pool;i>=1;i--)
        {
            int v=id[i];
            mn[v]=min(mn[v],mx[v]);
            if(mx[v] && fa[v])
                mx[fa[v]]=len[fa[v]];
        }
    }

    inline int calc()
    {
        int ans=0;
        for(int i=1;i<=pool;i++)
            ans=max(ans,mn[i]);
        return ans;
    }
}koishi;

int main()
{
    koishi.init();

    char s[N];
    scanf("%s",s+1);
    int len=strlen(s+1);
    for(int i=1;i<=len;i++)
        koishi.add(s[i]-'a');
    koishi.topsort(strlen(s+1));

    while(scanf("%s",s+1)!=EOF)
        koishi.run(s);

    printf("%d\n",koishi.calc());

    return 0;
}
//spoj还我青春!!!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值