SPOJ - LCS2 - Longest Common Substring II

190 篇文章 2 订阅
27 篇文章 0 订阅

题面

题意

给出最多十个字符串,求它们的最长公共子串.

做法

大致思路是对第一个字符串建出后缀自动机后,每个字符串都去匹配,对于自动机上每一个节点都用一个临时数组求出它所能匹配到的最长后缀长度,每个字符串匹配完后临时数组与上一次匹配后的结束状态取一个最小值.
一开始TLE了一次,WA了一次.
TLE那次我更新临时数组上的某个点时,沿着它的父节点向上更新了所有点,这样对于极限数据将会退化成O(n^2),因此会T.
后来发现我可以先对每一个点求出当前点的最长匹配后缀的长度,最后再沿着它的父节点节点向上更新.
但要注意后缀自动机上节点的父节点的标号不一定要比它小因为nq节点是q点的父节点,所以要先求出根据父节点值排的拓扑序,再一次更新父亲节点,这也正是我WA的原因.

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100100
using namespace std;

int last,tt,tmp[N<<1],ans,dfn[N<<1],dd;
char str[N];
bool vis[N<<1];
struct Node
{
    int son[26],fail,len,pp;
}node[N<<1];

inline void add(int u)
{
    int np=++tt,p,q,nq;
    for(p=last;p!=-1&&!node[p].son[u];p=node[p].fail) node[p].son[u]=np;
    node[np].len=node[last].len+1;
    node[np].pp=node[np].len;
    last=np;
    if(p==-1)
    {
        node[np].fail=0;
        return;
    }
    q=node[p].son[u];
    if(node[q].len==node[p].len+1)
    {
        node[np].fail=q;
        return; 
    }
    nq=++tt;
    node[nq]=node[q];
    node[nq].len=node[p].len+1;
    node[nq].pp=node[nq].len;
    node[q].fail=node[np].fail=nq;
    for(;p!=-1&&node[p].son[u]==q;p=node[p].fail) node[p].son[u]=nq;
}

inline void find()
{
    int i,t,now=0,l=0,u,k;
    for(i=1,t=strlen(str+1);i<=t;i++)
    {
        u=str[i]-'a';
        for(;now&&!node[now].son[u];now=node[now].fail,l=node[now].len);
        if(node[now].son[u])
        {
            l++;
            now=node[now].son[u];
            tmp[now]=max(tmp[now],l);
            continue;
        }
    }
}

void in(int u)
{
    if(vis[u]) return;
    in(node[u].fail);
    vis[u]=1;
    dfn[++dd]=u;
}

int main()
{
    int i,j,t;
    scanf("%s",str+1);
    node[0].fail=-1;
    for(i=1,t=strlen(str+1);i<=t;i++)
    {
        add(str[i]-'a');
    }
    vis[0]=1;
    for(i=1;i<=tt;i++)
    {
        in(i);
    }
    while(~scanf("%s",str+1))
    {
        memset(tmp,0,sizeof(tmp));
        find();
        for(j=tt;j>=1;j--)
        {
            i=dfn[j];
            tmp[node[i].fail]=max(tmp[node[i].fail],tmp[i]);
            node[i].pp=min(node[i].pp,tmp[i]);
        }
    }
    for(i=1;i<=tt;i++)
    {
        ans=max(ans,node[i].pp);
    }
    cout<<ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值