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;
}

原文地址:https://www.cnblogs.com/KirinSB/p/11667401.html

时间: 2024-07-29 17:54:48

SPOJ - LCS2 Longest Common Substring II(后缀自动机)题解的相关文章

spoj 1812 LCS2 - Longest Common Substring II (后缀自动机)

spoj 1812 LCS2 - Longest Common Substring II 题意: 给出最多n个字符串A[1], ..., A[n], 求这n个字符串的最长公共子串. 限制: 1 <= n <= 10 |A[i]| <= 1e5 思路: 和spoj 1811 LCS差不多的做法 把其中一个A建后缀自动机 考虑一个状态s, 如果A之外的其他串对它的匹配长度分别是a[1], a[2], ..., a[n - 1], 那么min(a[1], a[2], ..., a[n - 1]

SPOJ 1812 LCS2 - Longest Common Substring II (后缀自动机)【两种做法】

手动博客搬家: 本文发表于20181217 23:54:35, 原地址https://blog.csdn.net/suncongbo/article/details/85058680 人生第一道后缀自动机. 说实话SAM我还没学多么明白. 但是题还是要做的. 说起来这玩意真的很妙.可惜我智商低理解不了. 再次验证了代码能力菜到没边.hyw 30min写完我写2.5h. 题目链接 (洛谷) https://www.luogu.org/problemnew/show/SP1812 题目大意 给\(n

【SPOJ】Longest Common Substring(后缀自动机)

[SPOJ]Longest Common Substring(后缀自动机) 题面 Vjudge 题意:求两个串的最长公共子串 题解 \(SA\)的做法很简单 不再赘述 对于一个串构建\(SAM\) 另外一个串在\(SAM\)上不断匹配 最后计算答案就好了 匹配方法: 如果\(trans(s,c)\)存在 直接沿着\(trans\)走就行,同时\(cnt++\) 否则沿着\(parent\)往上跳 如果存在\(trans(now,c),cnt=now.longest+1\) 否则,如果不存在可行的

spoj 1812 LCS2 - Longest Common Substring II (后缀自己主动机)

spoj 1812 LCS2 - Longest Common Substring II 题意: 给出最多n个字符串A[1], ..., A[n], 求这n个字符串的最长公共子串. 限制: 1 <= n <= 10 |A[i]| <= 1e5 思路: 和spoj 1811 LCS几乎相同的做法 把当中一个A建后缀自己主动机 考虑一个状态s, 假设A之外的其它串对它的匹配长度各自是a[1], a[2], ..., a[n - 1], 那么min(a[1], a[2], ..., a[n -

Spoj LCS2 - Longest Common Substring II

题目描述 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 t

SPOJ LCS2 - Longest Common Substring II 字符串 SAM

原文链接http://www.cnblogs.com/zhouzhendong/p/8982484.html 题目传送门 - SPOJ LCS2 题意 求若干$(若干<10)$个字符串的最长公共连续子串长度. 串长$\leq 100000$ 题解 建议在做本题之前,先去做SPOJ LCS,本题是其升级版. 题解链接 - SPOJ LCS - http://www.cnblogs.com/zhouzhendong/p/8982392.html 对于本题,我们只需要保持一下之后每一个串在第一个串的$

SPOJ LCS Longest Common Substring(后缀自动机)题解

题意: 求两个串的最大\(LCS\). 思路: 把第一个串建后缀自动机,第二个串跑后缀自动机,如果一个节点失配了,那么往父节点跑,期间更新答案即可. 代码: #include<set> #include<map> #include<cmath> #include<queue> #include<bitset> #include<string> #include<cstdio> #include<vector>

spoj1812 Longest Common Substring II( 后缀自动机 )

贴个代码... --------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn = 200009; const int cn = 26; struct Node { Node *fa, *ch[cn]; int le

【SPOJ】Longest Common Substring II (后缀自动机)

[SPOJ]Longest Common Substring II (后缀自动机) 题面 Vjudge 题意:求若干个串的最长公共子串 题解 对于某一个串构建\(SAM\) 每个串依次进行匹配 同时记录\(f[i]\)表示走到了\(i\)节点 能够匹配上的最长公共子串的长度 当然,每个串的\(f[i]\)可以更新\(f[i.parent]\) 所以需要拓扑排序 对于每个串求出每个节点的最长匹配 然后对他们取\(min\),表示某个节点大家都能匹配的最长长度 最后对于所有点的值都取个\(max\)