1. 程式人生 > >Longest Common Substring II-字尾自動機

Longest Common Substring II-字尾自動機

LCS2 - Longest Common Substring II

Description

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.

Input

The input contains at most 10 lines, each line consists of no more than 100000 lowercase letters, representing a string.

Output

The length of the longest common substring. If such string doesn’t exist, print “0” instead.

Example

Input:

alsdfkjfjkdsal
fdjskalajfkdsla
aaaajfaaaa

Output:

2

Notice: new testcases added

//不開心.jpg
┏┻┻┻┻┻┻┻┓
┃ ┏┓   ┏┓ ┃
┃ ┗┛ ^ ┗┛ ┃
┗┏━┓━┏━┓┛
  ┗━┛  ┗━┛
     |   |

//spoj有毒.jpg

spoj還我青春!!!
在此向所有做這道題的人一個提醒:樣例錯了!!!
樣例輸出是3!!!不是2!!!
調了我一晚上啊啊啊

(╯‵□′)╯︵┻━┻

發洩完了迴歸正文:
思路:
先對第一個串建字尾自動機~
然後跟1811一樣的思路,但由於是多個串,所以需要我們維護每個節點能匹配的最短匹配值,最後取其最大值作為答案。

具體方法是在每次匹配時開一個數組記錄當前匹配過程中當前節點匹配到的最長匹配值,在每次結束匹配後用它的值更新最短匹配值即可。

需要注意的是,在更新時,需要對每個經過的節點,更新它們的父親為其len值並傳遞下去,因為根據字尾自動機的性質,經過了當前節點,其父親相當於也被經過了一次,只是在匹配中實際走到的是其等價的字尾節點而不是這個父親節點。

問題來了:怎麼保證剛好傳遞完所有的理應被經過的點?
其實,把所有節點根據長度做一遍基數排序,按順序更新就可以保證不會出錯,跑出來的更新順序一定是拓撲序,剛好滿足傳遞完所有的要傳遞的點。
為什麼呢?
因為建自動機時拓撲序在後的節點len值更大啊~
所以就沒有然後了~

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>

using namespace std;

const int N=200233;

struct SAM
{
    int next[N][26],fa[N],len[N];
    int pool,u,id[N],b[N];
    int mn[N],mx[N];

    inline void init()
    {
        pool=u=1;
        memset(next,0,sizeof(next));
        memset(len,0,sizeof(len));
        memset(mn,127,sizeof(mn));
        memset(fa,0,sizeof(fa));
        memset(id,0,sizeof(id));
    }

    inline void add(int v)
    {
        int now=++pool;
        len[now]=len[u]+1;
        while(!next[u][v] && u)
            next[u][v]=now,u=fa[u];

        if(!u)
            fa[now]=1;
        else
        {
            int q=next[u][v];
            if(len[q]==len[u]+1)
                fa[now]=q;
            else
            {
                int newq=++pool;
                len[newq]=len[u]+1;
                fa[newq]=fa[q];
                memcpy(next[newq],next[q],sizeof(next[q]));

                fa[q]=fa[now]=newq;

                while(next[u][v]==q)
                    next[u][v]=newq,u=fa[u];
            }
        }
        u=now;
    }

    inline void topsort(int lenn)
    {
        for(int i=1;i<=pool;i++)
            ++b[len[i]];
        for(int i=1;i<=lenn;i++)
            b[i]+=b[i-1];
        for(int i=1;i<=pool;i++)
            id[b[len[i]]--]=i;
    }

    inline void run(char *s)
    {
        int now=1;
        int tmp=0;
        int lenn=strlen(s+1);

        memset(mx,0,sizeof(mx));
        for(int i=1;i<=lenn;i++)
        {
            while(!next[now][s[i]-'a'] && now)
                now=fa[now],tmp=len[now];
            if(!now)
                now=1,tmp=0;
            else
                tmp++,now=next[now][s[i]-'a'];

            mx[now]=max(mx[now],tmp);
        }

        for(int i=pool;i>=1;i--)
        {
            int v=id[i];
            mn[v]=min(mn[v],mx[v]);
            if(mx[v] && fa[v])
                mx[fa[v]]=len[fa[v]];
        }
    }

    inline int calc()
    {
        int ans=0;
        for(int i=1;i<=pool;i++)
            ans=max(ans,mn[i]);
        return ans;
    }
}koishi;

int main()
{
    koishi.init();

    char s[N];
    scanf("%s",s+1);
    int len=strlen(s+1);
    for(int i=1;i<=len;i++)
        koishi.add(s[i]-'a');
    koishi.topsort(strlen(s+1));

    while(scanf("%s",s+1)!=EOF)
        koishi.run(s);

    printf("%d\n",koishi.calc());

    return 0;
}
//spoj還我青春!!!