1. 程式人生 > >SPOJ1812 Longest Common Substring II

SPOJ1812 Longest Common Substring II

tdi character getchar strlen gist his 沒有 log most

題意

A string is finite sequence of characters over a non-empty finite set Σ.

In this problem, Σ is the set of lowercase letters.

Substring, also called factor, is a consecutive sequence of characters occurrences at least once in a string.

Now your task is a bit harder, for some given strings, find the length of the longest common substring of them.

Here common substring means a substring of two or more strings.

給定n個串,求它們的最長公共子串。

at most 10 lines,no more than 100000

分析

參照iwt的題解。

本題容易看出就是分別將所有串的所有匹配長度記錄在狀態上,然後取所有串記錄值的min,後再對所有狀態取max。

但是不要忘記了一點:更新parent樹的祖先。

為什麽呢?首先如果子樹被匹配過了,那麽長度一定大於任意祖先匹配的長度(甚至有些祖先匹配長度為0!為什麽呢,因為我們在匹配的過程中,只是找到一個子串,可能還遺漏了祖先沒有匹配到,這樣導致了祖先的記錄值為0,那麽在對對應狀態取min的時候會取到0,這樣就wa了。而且註意,如果匹配到了當前節點,那麽祖先們一定都可以賦值為祖先的length!因為當前節點的length大於任意祖先。(

比如數據

acbbc
bc
ac

答案應該是1沒錯吧。如果沒有更新祖先,那麽答案會成0。

這個多想想就行了。

所以以後記住:對任意多串匹配時,凡是對同一個狀態取值時,要註意當前狀態的子樹是否比當前狀態記錄的值優。

時間復雜度:線性。

代碼

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<typename T>il T read()
{
    rg T data=0,w=1;
    rg char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        data=data*10+ch-'0';
        ch=getchar();
    }
    return data*w;
}
template<typename T>il T read(rg T&x)
{
    return x=read<T>();
}
typedef long long ll;

co int N=2e5;
namespace SAM
{
    int tot,last;
    int ch[N][26],fail[N]={-1},len[N];
    void extend(int k)
    {
        int cur=++tot;
        len[cur]=len[last]+1;
        int p=last;
        while(~p&&!ch[p][k])
        {
            ch[p][k]=cur;
            p=fail[p];
        }
        if(p==-1)
            fail[cur]=0;
        else
        {
            int q=ch[p][k];
            if(len[q]==len[p]+1)
                fail[cur]=q;
            else
            {
                int clone=++tot;
                std::copy(ch[q],ch[q]+26,ch[clone]);
                fail[clone]=fail[q],len[clone]=len[p]+1;
                while(~p&&ch[p][k]==q)
                {
                    ch[p][k]=clone;
                    p=fail[p];
                }
                fail[cur]=fail[q]=clone;
            }
        }
        last=cur;
    }
    int c[N],id[N],mx[N],arr[N];
    void build(char s[],int n)
    {
        for(int i=0;i<n;++i)
            extend(s[i]-'a');
        for(int i=0;i<=tot;++i)
            ++c[len[i]];
        for(int i=1;i<=n;++i)
            c[i]+=c[i-1];
        for(int i=0;i<=tot;++i)
            id[--c[len[i]]]=i; // edit 1:--c for 0
        std::copy(len,len+tot+1,mx);
    }
    void find(char s[],int n)
    {
        int p=0,l=0;
        for(int i=0;i<n;++i)
        {
            int k=s[i]-'a';
            if(ch[p][k])
                p=ch[p][k],++l;
            else
            {
                while(~p&&!ch[p][k])
                    p=fail[p];
                if(p==-1)
                    p=l=0;
                else
                    l=len[p]+1,p=ch[p][k];
            }
            arr[p]=std::max(arr[p],l);
        }
        for(int i=tot;i>=0;--i)
        {
            int p=id[i];
            mx[p]=std::min(mx[p],arr[p]);
            if(arr[p]&&fail[p])
                arr[fail[p]]=len[fail[p]];
            arr[p]=0;
        }
    }
    int getans()
    {
        int ans=0;
        for(int i=0;i<=tot;++i)
            ans=std::max(ans,mx[i]);
        return ans;
    }
}
char buf[N];

int main()
{
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    scanf("%s",buf);
    SAM::build(buf,strlen(buf));
    while(~scanf("%s",buf))
        SAM::find(buf,strlen(buf));
    printf("%d\n",SAM::getans());
    return 0;
}

SPOJ1812 Longest Common Substring II