SPOJ 1812 Longest Common Substring II(后缀自动机)

题目链接:http://www.spoj.com/problems/LCS2/

题意:给定n个串求最长公共substring。

思路:将第一个串建立自动机。后面的每个串在自动机上跑一遍。节点记录两个信息,所有串的最小值Min和当前串的最大值curLen。当前串跑完之后要用后面的curLen更新前面的curLen,然后用curLen更新Min。





const int KIND=26;

struct SAM

{

    SAM *son[KIND],*pre;

    int len,Min,curLen;

};



SAM sam[N],*head,*last,*b[N];

int d[N],cnt,f[N],len;

char s[N];



void initSAM()

{

    head=last=&sam[0];

    cnt=1;

}





void insert(int x)

{

    SAM *p=&sam[cnt++],*u=last;

    p->len=last->len+1;

    last=p;

    for(;u&&!u->son[x];u=u->pre) u->son[x]=p;

    if(!u) p->pre=head;

    else if(u->son[x]->len==u->len+1) p->pre=u->son[x];

    else

    {

        SAM *r=&sam[cnt++],*q=u->son[x];

        *r=*q; r->len=u->len+1;

        p->pre=q->pre=r;

        for(;u&&u->son[x]==q;u=u->pre) u->son[x]=r;

    }

}







int main()

{

    initSAM(); RD(s); len=strlen(s);

    int i;

    FOR0(i,len) insert(s[i]-'a');

    FOR0(i,cnt) d[sam[i].len]++;

    FOR1(i,len) d[i]+=d[i-1];

    FOR0(i,cnt) b[--d[sam[i].len]]=&sam[i];

    int x,L;

    SAM *p;

    FOR0(i,cnt) sam[i].Min=sam[i].len;

    while(scanf("%s",s)!=EOF)

    {

        p=head; len=strlen(s); L=0;

        FOR0(i,len)

        {

            x=s[i]-'a';

            if(p->son[x]) L++,p=p->son[x];

            else

            {

                while(p&&!p->son[x]) p=p->pre;

                if(!p) L=0,p=head;

                else L=p->len+1,p=p->son[x];

            }

            if(L>p->curLen) p->curLen=L;

        }

        FORL0(i,cnt-1)

        {

            p=b[i];

            if(p->curLen<p->Min) p->Min=p->curLen;

            if(p->pre&&p->pre->curLen<p->curLen) p->pre->curLen=p->curLen;

            p->curLen=0;

        }

    }

    int ans=0;

    FOR0(i,cnt) if(sam[i].Min>ans) ans=sam[i].Min;

    PR(ans);

    return 0;

}

  

你可能感兴趣的:(substring)