【后缀自动机】SPOJ(LCS)[Longest Common Substring]题解

来源:互联网 发布:高级sql生成选项 编辑:程序博客网 时间:2024/05/22 03:22

题目概述

给出两个串A和B,求A和B的最长公共子串。

解题报告

这道题是SAM的经典应用,首先先提醒一下不要一看到LCS就想到最长公共子序列去了,这里是最长公共字串……
SAM真的是很神的字符串算法啊,不仅复杂度是线性的,还非常好写,但是真的太神了,所以很多可以用SAM的地方还需要各种转化。
我们先对A串建立SAM,那么现在我们就可以识别A的所有子串了。我们按顺序枚举B的前缀i,判断该前缀的后缀是否出现在A中。先记录p表示处理前缀i-1时到达的状态,再记录len表示当前B的前缀能匹配到的后缀的长度。如果p能够沿着B[i]走下去,那么说明B的前缀i能够匹配到的后缀的长度就是len+1(从前面延伸过来),否则p就必须沿着father树向上走,走到能继续扩展为止,如果能扩展,那么len=p->MAX+1,p=p->son[B[i]],否则如果最终并不能扩展,就说明p=root,len=0。

示例程序

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxl=250000,maxi=26;char A[maxl+5],B[maxl+5];struct SAM{    struct node    {        node *son[maxi],*fa;int MAX;        node(int M) {MAX=M;fa=0;memset(son,0,sizeof(son));}    };    typedef node* P_node;    P_node ro,lst;    SAM() {ro=new node(0);lst=ro;}    void Insert(char ch)    {        int ID=ch-'a';P_node p=lst,np=new node(lst->MAX+1);        while (p&&!p->son[ID]) p->son[ID]=np,p=p->fa;        if (!p) np->fa=ro; else        {            P_node q=p->son[ID];            if (p->MAX+1==q->MAX) np->fa=q; else            {                P_node nq=new node(p->MAX+1);                memcpy(nq->son,q->son,sizeof(q->son));                nq->fa=q->fa;q->fa=nq;np->fa=nq;                while (p&&p->son[ID]==q) p->son[ID]=nq,p=p->fa;            }        }        lst=np;    }    void make_SAM(char *s) {for (int i=1;s[i];i++) Insert(s[i]);}    int LCS(char *s) //求此后缀自动机对应字符串与s的LCS    {        int ans=0,len=0;P_node p=ro;        for (int i=1;s[i];i++)        {            int ID=s[i]-'a';            if (p->son[ID]) p=p->son[ID],len++; else            {                while (p&&!p->son[ID]) p=p->fa;                if (!p) p=ro,len=0; else len=p->MAX+1,p=p->son[ID];            }            ans=max(ans,len); //从所有len中刷出最优解        }        return ans;    }};SAM sam;char readc(){    static char buf[100000],*l=buf,*r=buf;    if (l==r) r=(l=buf)+fread(buf,1,10000,stdin);    if (l==r) return EOF; else return *l++;}int reads(char *s){    int len=0;char ch=readc();if (ch==EOF) return EOF;    while ('z'<ch||ch<'a') ch=readc();s[++len]=ch;    while ('a'<=s[len]&&s[len]<='z') s[++len]=readc();s[len--]=0;    return len;}int main(){    freopen("program.in","r",stdin);    freopen("program.out","w",stdout);    reads(A);reads(B);sam.make_SAM(A);    printf("%d\n",sam.LCS(B));    return 0;}
阅读全文
0 0
原创粉丝点击