spoj 1811 LCS (后缀自动机 SAM)

http://www.spoj.com/problems/LCS/

题意:求两个字符串A,B的最长公共子串。字符串长度不超过250000。

 

思路:这应该算是后缀自动机的经典应用了吧,我们先构造A的SAM,然后用A的SAM一次读入B的每一个字符,初始时状态在root处,此时最大匹配数为tmp=0,(这里的最大匹配数是指以当前读入的字符结尾,往前能匹配的最大长度),设当前到达的状态为p,最大匹配数为tmp,读入的字符为x,若p->go[x]!=NULL,则说明可从当前状态读入一个字符x到达下一个状态,则tmp++,p=p->go[x],否则,找到p的第一个祖先s,s->go[x]!=NULL,若s不存在,则说明以x结尾的字符串无法和A串的任何位置匹配,则设tmp=0,p=root。否则,设tmp=s->tmp+1(因为我们不算x的话已经到达了状态p,这说明对于p的任意祖先已经匹配完毕),p=s->go[x]。我们求tmp所达到的最大值即为所求。代码如下:

#include <iostream>
#include <string.h>
#include <algorithm>
#include <stdio.h>
#define maxn 500010
using namespace std;
char str[maxn>>1];
struct node
{
    node *par,*go[26];
    int val;
}*root,*tail,que[maxn],*top[maxn];
int tot,len,c[maxn];
void add(int c,int l)
{
    node *np=&que[tot++],*p=tail;
    np->val=l;
    while(p&&p->go[c]==NULL)
    {
        p->go[c]=np;
        p=p->par;
    }
    if(p==NULL)
    np->par=root;
    else
    {
        node *q=p->go[c];
        if(q->val==p->val+1)
        np->par=q;
        else
        {
            node *nq=&que[tot++];
            *nq=*q;
            nq->val=p->val+1;
            np->par=q->par=nq;
            while(p&&p->go[c]==q)
            {
                p->go[c]=nq;
                p=p->par;
            }
        }
    }
    tail=np;
}
void init(int n)
{
    for(int i=0;i<=n;i++)
    {
        memset(que[i].go,0,sizeof(que[i].go));
        que[i].val=0;
    }
    tot=0;
    len=1;
    root=tail=&que[tot++];
}
void solve()
{
    int i;
    scanf("%s",str);
    int l=strlen(str),ans=0,tmp=0;
    node *p=root;
    for(i=0;i<l;i++)
    {
        int x=str[i]-'a';
        if(p->go[x])
        {
            p=p->go[x];
            tmp++;
        }
        else
        {
            while(p&&p->go[x]==NULL)
            p=p->par;
            if(p)
            {
                tmp=p->val+1;
                p=p->go[x];
            }
            else
            {
                p=root;
                tmp=0;
            }
        }
        if(tmp>ans)
        ans=tmp;
    }
    printf("%d\n",ans);
}
int main()
{
    //freopen("dd.txt","r",stdin);
    scanf("%s",str);
    int l=strlen(str);
    init(l*2);
    for(int i=0;i<l;i++)
    add(str[i]-'a',len++);
    solve();
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值