标签: 后缀自动机

[SPOJ-LCS2]Longest Common Substring II 题解

[SPOJ-LCS2]Longest Common Substring II 题解

题目地址:SPOJ:SPOJ.com – Problem LCS2、洛谷:【SP1812】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 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.

题意简述

给定一些字符串,求出它们的最长公共子串。

输入输出格式

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

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

输入输出样例

输入样例#1:

alsdfkjfjkdsal
fdjskalajfkdsla
aaaajfaaaa

输出样例#1:

2

题解

考虑一个SAM求两个串LCS的做法,是把一个串建SAM另一个串在上面跳。在这一题,我们仍然可以用一个串建LCS,其他串在SAM上跳的方法,在跳的过程中,在每个经过的节点上记下以该节点结尾的LCS长度的最大值,由于后缀链接可以传递LCS,需要用该节点的最大值沿后缀链接更新前缀树中父亲节点的最大值。最后统计出每个节点在所有串中LCS情况的最小值的最大值即可。
复杂度O(n)

代码

// Code by KSkun, 2018/6
#include <cstdio>
#include <cctype>
#include <cstring>

#include <algorithm>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2)
        ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1; register char c = fgc();
    for (; !isdigit(c); c = fgc()) if (c == '-') neg = -1;
    for (; isdigit(c); c = fgc()) res = (res << 1) + (res << 3) + c - '0';
    return res * neg;
}

const int MAXN = 100005;

struct SuffixAutomaton {
    int last, tot, ch[MAXN << 1][26], nxt[MAXN << 1], len[MAXN << 1];
    SuffixAutomaton() {
        last = tot = 1;
    }
    inline void extend(int c) {
        int u = ++tot, v = last; 
        len[u] = len[v] + 1;
        for(; v && !ch[v][c]; v = nxt[v]) ch[v][c] = u;
        if(!v) {
            nxt[u] = 1;
        } else if(len[ch[v][c]] == len[v] + 1) {
            nxt[u] = ch[v][c];
        } else {
            int n = ++tot, o = ch[v][c]; len[n] = len[v] + 1;
            memcpy(ch[n], ch[o], sizeof(ch[o]));
            nxt[n] = nxt[o]; nxt[o] = nxt[u] = n;
            for(; v && ch[v][c] == o; v = nxt[v]) ch[v][c] = n;
        }
        last = u;
    } 
} sam;

char str[MAXN];
int a[MAXN << 1], c[MAXN << 1], mx[MAXN << 1], ans[MAXN << 1];

int main() {
    memset(ans, 0x3f, sizeof(ans));
    scanf("%s", str + 1);
    for(int i = 1; str[i]; i++) {
        sam.extend(str[i] - 'a');
    }
    for(int i = 1; i <= sam.tot; i++) c[sam.len[i]]++;
    for(int i = 1; i <= sam.tot; i++) c[i] += c[i - 1];
    for(int i = 1; i <= sam.tot; i++) a[c[sam.len[i]]--] = i;
    while(scanf("%s", str + 1) != EOF) {
        int len = 0, p = 1, c; 
        for(int i = 1; str[i]; i++) {
            c = str[i] - 'a';
            while(p && !sam.ch[p][c]) {
                p = sam.nxt[p]; len = sam.len[p];
            }
            if(p) {
                len++; p = sam.ch[p][c]; mx[p] = std::max(mx[p], len);
            } else {
                p = 1; len = 0;
            }
        }
        for(int i = sam.tot; i; i--) {
            int p = a[i];
            mx[sam.nxt[p]] = std::max(mx[sam.nxt[p]], std::min(mx[p], sam.len[sam.nxt[p]]));
            ans[p] = std::min(ans[p], mx[p]); mx[p] = 0;
        }
    }
    int res = 0;
    for(int i = 1; i <= sam.tot; i++) {
        res = std::max(res, ans[i]);
    }
    printf("%d", res);
    return 0;
}