SPOJ - LCS2 Longest Common Substring II(后缀自动机)题解

题意:

\(n\)个串的最大\(LCS\)

思路:

把第一个串建后缀自动机,然后枚举所有串。对于每个串,求出这个串在\(i\)节点的最大匹配为\(temp[i]\)(当前串在这个节点最多取多少),然后我们求出最终所有串在\(i\)节点的匹配最小值\(mn[i]\)(即为所有串在\(i\)节点都能取到多少),答案即为\(max\{min[i]\}\)
但是我们能发现,如果我们更新了\(temp[i]\),那么其实\(fa[i]\)\(temp[fa[i]]\)也应该要更新,因为父节点是我的后缀子串,只是我没有走过去而已,并且\(temp[fa[i]] = max(temp[fa[i]], \ max(temp[i],\ mxlen[fa[i]]))\),因为父节点的匹配长度不能超过他本身长度。
为了能线性实现如上操作,我们按照\(mxlen\)大小桶排,因为父节点的\(mxlen\)一定小于子节点,那么我直接倒着更新就能保证我更新父节点时自己一定已经更新过了。
黄某讲的挺好,点击前往

代码:

#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<bitset>
#include<string>
#include<cstdio>
#include<vector>
#include<cstring>
#include <iostream>
#include<algorithm>
using namespace std;
const int maxn = 100000 + 10;
typedef long long ll;
const ll MOD = 1e9 + 7;
int x[maxn << 1], rk[maxn << 1];
//桶排,按照len从小到大排序节点
struct SAM{
    int node[maxn << 1][26], fa[maxn << 1], mxlen[maxn << 1];
    int mn[maxn << 1];
    int sz, last;
    int newnode(){
        ++sz;
        memset(node[sz], 0, sizeof(node[sz]));
        fa[sz] = mxlen[sz] = 0;
        return sz;
    }
    void init(){
        sz = 0;
        last = newnode();
    }
    void insert(int k){
        int p = last, np = last = newnode();
        mxlen[np] = mxlen[p] + 1;
        for(; p && !node[p][k]; p = fa[p])
            node[p][k] = np;
        if(p == 0){
            fa[np] = 1;
        }
        else{
            int t = node[p][k];
            if(mxlen[t] == mxlen[p] + 1){
                fa[np] = t;
            }
            else{
                int nt = newnode();
                memcpy(node[nt], node[t], sizeof(node[t]));
                fa[nt] = fa[t];
                mxlen[nt] = mxlen[p] + 1;
                fa[np] = fa[t] = nt;
                for(; p && node[p][k] == t; p = fa[p])
                    node[p][k] = nt;
            }
        }
    }
    void Sort(int len){
        //桶排,按照len从小到大排序节点
        for(int i = 1; i <= sz; i++) x[mxlen[i]]++;
        for(int i = 1; i <= len; i++) x[i] += x[i - 1];
        for(int i = 1; i <= sz; i++) rk[x[i]] = i;

        for(int i = 1; i <= sz; i++) mn[i] = mxlen[i];
    }
    int tmp[maxn << 1];
    void build(char *s){
        for(int i = 0; i <= sz; i++) tmp[i] = 0;
        int len = strlen(s);
        int pos = 1;
        int ret = 0;
        for(int i = 0; i < len; i++){
            int c = s[i] - 'a';
            while(pos && node[pos][c] == 0){
                pos = fa[pos];
                ret = mxlen[pos];
            }
            if(pos == 0){
                ret = 0;
                pos = 1;
            }
            else{
                pos = node[pos][c];
                ret++;
            }
            tmp[pos] = max(tmp[pos], ret);
        }
        for(int i = sz; i >= 1; i--){
            int c = rk[i];
            tmp[fa[c]] = min(max(tmp[fa[c]], tmp[c]), mxlen[fa[c]]);
        }
        for(int i = 1; i <= sz; i++){
            mn[i] = min(mn[i], tmp[i]);
        }
    }
    void query(){
        int ret = 0;
        for(int i = 1; i <= sz; i++) ret = max(ret, mn[i]);
        printf("%d\n", ret);
    }

}sam;
char s[maxn];
int main(){
    sam.init();
    scanf("%s", s);
    int len = strlen(s);
    for(int i = 0; i < len; i++) sam.insert(s[i] - 'a');
    sam.Sort(len);
    sam.build(s);
    while(~scanf("%s", s)){
        sam.build(s);
    }
    sam.query();
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/KirinSB/p/11667401.html