spoj 1811 LCS (后缀自动机 SAM)

来源:互联网 发布:斗鱼领鱼丸软件 编辑:程序博客网 时间:2024/04/29 16:30

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 500010using 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;}


 

原创粉丝点击