SPOJ LCS2 - Longest Common Substring II 后缀自动机 经典模板题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lifelikes/article/details/81449265

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

题意: 给出k个串 求这k个串的最长公共子串

这是一道经典的后缀自动机模板题。
这里利用了后缀自动机的一些性质
1。 每个状态存储了一连串后缀的集合。
2。 整个后缀自动机所有状态可以表示整个串的所有子串
3。通过前驱节点连接的一条链上的所有点,可以表示一个子串的所有后缀。
那么,对于这个题,我们可以对一个串建立后缀自动机,然后让其他串去匹配。
每匹配一个串,我们就可以更新这个串的所有状态对应最长公共子串的长度。
然后取全局最大即可。
要注意的是,我们不可能每次都匹配到后缀自动机的所有状态,这样就会导致某些状态被遗漏,所以,我们需要通过儿子节点去更新父亲节点。
这里可以通过dfs,拓扑序,或者基数排序(很对题解都是用这个方法,我完全想不到可以直接这么排序,他们太强了)更新。
中间有一些细节,就不一一讲述了,我在代码中将我觉得要注意的地方标注了一下。
总的来说 还是一道简单模板题。。

#include <bits/stdc++.h>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define x first
#define y second
#define rep(i,a,b) for(int i=a;i<b;++i)
#define per(i,a,b) for(int i=a-1;i>=b;--i)
#define fuck(x) cout<<'['<<#x<<' '<<(x)<<']'
#define FIN freopen("in.txt","r",stdin);
using namespace std;
#define N 2000050
typedef long long LL;
char s[N];
int ch[N][27],len[N],link[N],rd[N],last,tot,rt,n;
int F[N],ans[N];
int cnt[N];
char str[N];
void add(int pos) {
    int x = s[pos] - 'a' + 1 , p = last , np = ++tot;
    last = np;
    len[np] = len[p] + 1;
    F[np] = 1LL;
    while (p && !ch[p][x]) ch[p][x] = np , p = link[p];
    if (!p)
        link[np] = rt;
    else {
        int q = ch[p][x];
        if (len[q] == len[p] + 1)
            link[np] = q;
        else {
            int nq = ++tot;
            len[nq] = len[p] + 1;
            memcpy(ch[nq],ch[q],sizeof(ch[q]));
            link[nq] = link[q];
            link[q] = link[np] = nq;
            while (p && ch[p][x] == q) ch[p][x] = nq , p = link[p];
        }
    }
    return ;
}
void getsize(){
    for (int i=1;i<=tot;i++) rd[ link[i] ]++;
    queue<int> q;
    for (int i=1;i<=tot;i++) if (!rd[i]) q.push(i);
    while (!q.empty()) {
        int u = q.front(); q.pop();
        F[ link[u] ] += F[u];
        cnt[link[u]] = max(cnt[link[u]],min(cnt[u],len[link[u]]));
        if (--rd[ link[u] ] == 0) q.push(link[u]);
    }
}
void update(){
    int lens = strlen(str);
    int now  =  rt;
    for(int i=1;i<=tot;i++){
        cnt[i]= 0;
    }
    // tmp表示当前状态最大长度(重点!)
    int tmp=0;
    for(int i=0;i<lens;i++){
        int x = str[i] - 'a' + 1;
        while(!ch[now][x] && now){
            now = link[now];
        }
        tmp = min(tmp,len[now]);
        if(ch[now][x]) tmp++;
        if(!now){
            now = rt;
        }else{
            cnt[ch[now][x]]=max(tmp,cnt[ch[now][x]]);
            now = ch[now][x];
        }
    }
    //  拓扑序更新父亲节点状态
    getsize();
    //  局部取最小
    for(int i=1;i<=tot;i++){
        ans[i]=min(ans[i],cnt[i]);
    }
}
void init(){
    rt = last = ++tot;
}
int main() {
    init();
    scanf("%s",s+1); n = strlen(s+1);
    for (int i=1;i<=n;i++) add(i);
    for(int i=1;i<=tot;i++){
        ans[i]=len[i];
    }
    while(~scanf("%s",str)){
        update();
    }
    int mins=-1;
    // 全局取最大
    for(int i=1;i<=tot;i++){
        mins=max(mins,ans[i]);
    }
    cout<<mins<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lifelikes/article/details/81449265