【再谈后缀自动机(入门)】[SPOJLCS2]Longest Common Substring II

题目大意

给出n个字符串n<=10,每个字符串长度小于100000,求它们的最长公共串的长度。

分析

关于后缀自动机

复习过程中再看后缀自动机,把许多初学的时候没弄懂的问题解决了。
首先要明确后缀自动机的每个节点所表示的是一个终点等价类,从根节点走到叶子节点就对应一个后缀,len表示这终点等价类中长度最长字符串的长度,这个中终点等价类中的字符串的长度连续,且长度短字符串的是长度长的的后缀,后缀边指向的节点所对应的字符串是这个节点对应的字符串的后缀。
后缀边所指向的节点所对应的终点等价类的终点包含当前节点的终点等价类对应的终点。

ufail=vendpos(u)endpos(v)len(v)max+1=len(u)min

在构建的过程中plast沿着后缀边走第一个有字符c转移的节点,qp经字符c转移而来的。我们要使q所对应的终点等价类增加c这个位置。

  • q>lenp>len+1,说明由p转移到q并不是q点所对应的最长的字符串但这却是能够增加这个终点的最长的字符串,q所对应的长于该长度的字符串都不能增加这个终点,,所以我们新建一个节点nq,将q的信息复制给他len,fail,chnqlen=plen+1qfial=nq,npfial=nq,并且沿着后缀边走,将所有有字符c转移且转移到q的转移重定向至nq
    为什么ch也要复制?
    因为这个节点对应的字符串加上转移的边所对应的字符形成的字符串的终点和这个节点的终点的没有关系,所以要保证这个节点的儿子节点所对应的字符串不变。
  • q>len=p>len+1,则npfial=q

这道题

每个节点保存两个值:当前字符串能够匹配这个节点对应的字符串的最大长度nmx,所有字符串能够匹配这个节点对应的字符串的最大长度的最小值mx
然后,每次匹配完之后要沿着后缀边更新nmx,否则在用nmx更新mx时会出错。

ans=max(pmx)

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 0x7fffffff
#define MAXN 100000
using namespace std;
void Read(int &x){
    char c;
    while(c=getchar(),c!=EOF)
        if(c>='0'&&c<='9'){
            x=c-'0';
            while(c=getchar(),c>='0'&&c<='9')
                x=x*10+c-'0';
            ungetc(c,stdin);
            return;
        }
}
struct node{
    int len,mx,nmx;
    node *ch[26],*fail;
}tree[MAXN*2+10],*tcnt=tree,*root=tcnt,*l=tcnt,*r[MAXN*2+10];
char s[MAXN+10];
int ans,b[MAXN+10];
void insert(char c){
    c-='a';
    node *p=l,*np=++tcnt,*q,*nq;
    l=np;
    np->len=p->len+1;
    while(p&&!p->ch[c])
        p->ch[c]=np,p=p->fail;
    if(!p)
        np->fail=root;
    else{
        q=p->ch[c];
        if(q->len==p->len+1)
            np->fail=q;
        else{
            nq=++tcnt;
            *nq=*q;
            nq->len=p->len+1;
            q->fail=nq;
            np->fail=nq;
            while(p&&p->ch[c]==q)
                p->ch[c]=nq,p=p->fail;
        }
    }
}
void read(){
    scanf("%s",s);
    for(int i=0;s[i];i++)
        insert(s[i]);
}
void solve(){
    int i,len,tot;
    node *p;
    for(p=tree;p<=tcnt;p++){
        p->mx=p->len,p->nmx=0;
        b[p->len]++;
    }
    for(i=1;s[i-1];i++)
        b[i]+=b[i-1];
    for(p=tree;p<=tcnt;p++)
        r[--b[p->len]]=p;
    tot=tcnt-tree;
    while(~scanf("%s",s)){
        p=root;
        len=0;
        for(i=0;s[i];i++){
            s[i]-='a';
            if(p->ch[s[i]]){
                p=p->ch[s[i]];
                p->nmx=max(p->nmx,++len);
            }
            else{
                while(p&&!p->ch[s[i]])
                    p=p->fail;
                if(!p)
                    p=root,len=0;
                else{
                    len=p->len+1;
                    p=p->ch[s[i]];
                    p->nmx=max(p->nmx,len);
                }
            }
        }
        for(i=tot;i;i--){
            r[i]->fail->nmx=max(r[i]->nmx,r[i]->fail->nmx);//不用和r[i]->len进行比较,因为不会影响更新r[i]->mx
            r[i]->mx=min(r[i]->mx,r[i]->nmx);
            r[i]->nmx=0;
        }
    }
    for(p=tree;p<=tcnt;p++)
        ans=max(ans,p->mx);
}
int main()
{
    read();
    solve();
    printf("%d\n",ans);
}
posted @ 2016-04-08 08:36  outer_form  阅读(284)  评论(0编辑  收藏  举报