SPOJ1811: LCS - Longest Common Substring

【传送门:SPOJ1811


简要题意:

  给出两个字符串,求出两个字符串的最长公共子串


题解:

  后缀自动机

  先用第一个字符串构建SAM,然后用第二个字符串去跑

  假设当前的公共子串长度为s,在SAM的状态为p,匹配到第二个字符串的第i个字符

  如果p存在连向第i个字符的边,则p=tr[p].son[st[i]-'a'+1],s++

  否则让p去跳fail直到找到存在连向第i个字符的边为止

  如果找不到,则p=root,s=0,不然p=tr[p].son[st[i]-'a'+1],s=tr[p].dep+1


参考代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
struct SAM
{
    int son[27],fail,dep;
}tr[510000];int root,cnt,last;
int a[310000];
void add(int k)
{
    int x=a[k];
    int np=++cnt,p=last;
    tr[np].dep=k;
    while(p!=0&&tr[p].son[x]==0) tr[p].son[x]=np,p=tr[p].fail;
    if(p==0) tr[np].fail=root;
    else
    {
        int q=tr[p].son[x];
        if(tr[q].dep==tr[p].dep+1) tr[np].fail=q;
        else
        {
            int nq=++cnt;tr[nq]=tr[q];
            tr[nq].dep=tr[p].dep+1;
            tr[q].fail=tr[np].fail=nq;
            while(p!=0&&tr[p].son[x]==q) tr[p].son[x]=nq,p=tr[p].fail;
        }
    }
    last=np;
}
char st[310000];
int main()
{
    scanf("%s",st+1);
    int len=strlen(st+1);
    cnt=root=last=1;
    for(int i=1;i<=len;i++) a[i]=st[i]-'a'+1,add(i);
    scanf("%s",st+1);
    len=strlen(st+1);
    for(int i=1;i<=len;i++) a[i]=st[i]-'a'+1;
    int p=root,s=0,ans=0;
    for(int i=1;i<=len;i++)
    {
        if(tr[p].son[a[i]]!=0) s++,p=tr[p].son[a[i]];
        else
        {
            while(p!=0&&tr[p].son[a[i]]==0) p=tr[p].fail;
            if(p==0) p=root,s=0;
            else s=tr[p].dep+1,p=tr[p].son[a[i]];
        }
        ans=max(ans,s);
    }
    printf("%d\n",ans);
    return 0;
}

 

posted @ 2018-04-12 11:39  Star_Feel  阅读(100)  评论(0编辑  收藏  举报