spoj 1812 LCS2 (后缀自动机)

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

题意:求多个串的最长公共子串。每个字符串不超过100000

 

思路:也是SAM的经典应用了,基本思路和LCS一样(如果不太清楚的可以先看看这里http://blog.csdn.net/dyx404514/article/details/8718963

首先还是构造第一个字符串的SAM,我们在SAM的每个状态里多维护几个值:mi表示不同的字符串在该状态匹配长度的最小值(为什么是最小值这个留给大家思考),ma表示当前正在与A进行匹配的字符串在该状态匹配长度的最大值。ma可以按LCS里的思路求tmp的时候求出,mi要在当前字符串匹配完之后再求。

具体方法是:将SAM拓扑排序后自底向上更新,设当前状态为p,如果p->mi>p->ma,则p->mi=p->ma,如果p有父亲,设q为p的父亲,若p->ma>q->ma,则由SAM的性质可知,p的最大匹配数可以向上传递给它的父亲,这里要注意的是q的最大匹配数不能超过q->val,所以我们还要判断p->ma是否大于q->val,最后要把p->ma赋值为0,攻下一个字符串匹配时使用。当所有的字符串全匹配完之后,我们取所有状态mi的最大值即为所求。代码如下:

#include <iostream>
#include <string.h>
#include <algorithm>
#include <stdio.h>
#define maxn 200010
#define inf 210000000
using namespace std;
char str[100010];
struct node
{
    node *par,*go[26];
    int mi,ma;
    int val;
}*root,*tail,que[maxn],*top[maxn];
int min(int a,int b)
{
    return a<b?a:b;
}
int max(int a,int b)
{
    return a>b?a:b;
}
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;
        que[i].mi=inf;
        que[i].ma=0;
    }
    tot=0;
    len=1;
    root=tail=&que[tot++];
}
void solve()
{
    int i,ans=0,tmp;
    memset(c,0,sizeof(c));
    for(i=0;i<tot;i++)
    c[que[i].val]++;
    for(i=1;i<len;i++)
    c[i]+=c[i-1];
    for(i=0;i<tot;i++)
    top[--c[que[i].val]]=&que[i];
    while(scanf("%s",str)!=EOF)
    {
        tmp=0;
        int l=strlen(str);
        node *p=root;
        for(i=0;i<l;i++)
        {
            int x=str[i]-'a';
            if(p->go[x])
            {
                tmp++;
                p=p->go[x];
            }
            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(p->ma<tmp)
            p->ma=tmp;
        }
        for(i=tot-1;i>=0;i--)
        {
            p=top[i];
            if(p->mi>p->ma)
            p->mi=p->ma;
            if(p->par)
            {
                node *q=p->par;
                q->ma=min(q->val,max(q->ma,p->ma));
            }
            p->ma=0;
        }
    }
    for(i=0;i<tot;i++)
    ans=max(ans,que[i].mi);
    printf("%d\n",ans);
}
int main()
{
    //freopen("dd.txt","r",stdin);
    scanf("%s",str);
    int l=strlen(str);
    init(2*l);
    for(int i=0;i<l;i++)
    {
        add(str[i]-'a',len++);
    }
    solve();
    return 0;
}


 

 

你可能感兴趣的:(spoj 1812 LCS2 (后缀自动机))