SPOJ 1812 LCS2 - Longest Common Substring II

这道题目要求在给定的多个字符串中找出最长的公共子串。输入包括最多10行,每行不超过100000个小写字母。输出是公共子串的长度,如果不存在则输出0。解决方案涉及构建SAM(suffix automaton)并使用tmp和mx数组来跟踪匹配信息。

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

http://www.elijahqi.win/archives/2857
题意翻译

题面描述 给定一些字符串,求出它们的最长公共子串 输入格式 输入至多

10

10 行,每行包含不超过

100000

100000 个的小写字母,表示一个字符串 输出格式 一个数,最长公共子串的长度 若不存在最长公共子串,请输出

0

0 。

题目描述

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.

输入输出格式

输入格式:

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

输出格式:

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

输入输出样例

输入样例#1: 复制

alsdfkjfjkdsal
fdjskalajfkdsla
aaaajfaaaa
输出样例#1: 复制

2

求多个串的最长公共子串 首先针对第一个串建立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);
    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、付费专栏及课程。

余额充值