[SPOJ1811]LCS - Longest Common Substring

LCS - Longest Common Substring

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 simple, for two 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 exactly two lines, each line consists of no more than 250000 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
Output:
3

Solution
这里就用到后缀自动机的一个天然功能——子串匹配
将一个串A建成后缀自动机,那么从初始状态出发,随便走合法状态形成的串都是A的子串
于是我们将A建成后缀自动机,从初始状态出发,按B的字符一位一位转移,设当前公共子串长度为len,转移到的状态为p,下一位字符为c,若p有c这个转移边,那么我们直接p=nxt[p][c],len++,如果没有,那我们就沿p的fa不断走直到发现有c这个转移位置,len变为len[p]+1(相当于我知道B串的某一部分已经在A串中匹配,现在下一位无法匹配,我就不断取B串的后缀直到发现下一位可以匹配)
对于某一个我们计算过的状态,显然它的答案应该也可以作为它的parent的结果,自底向上更新一遍就可以得到所有状态能匹配的最长长度了

Code

#include <bits/stdc++.h>
using namespace std;

#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define per(i, r, l) for (int i = (r); i >= (l); i++)
#define MS(_) memset(_, 0, sizeof(_))
#define MP make_pair
#define PB push_back
inline void ckmin(int &x, int y) { if (x > y) x = y; }
inline void ckmax(int &x, int y) { if (x < y) x = y; }

const int N = 500100;
char A[N], B[N];
int n, m;
int root, last, cnt = 0;
int nxt[N][26], len[N], fa[N];

inline void insert(int c){
    int np = ++cnt, p = last; last = np;
    len[np] = len[p] + 1;
    for (; p && !nxt[p][c]; nxt[p][c] = np, p = fa[p]);
    if (!p) fa[np] = root;
    else if (len[nxt[p][c]] == len[p]+1) fa[np] = nxt[p][c];
    else{
        int nq = ++cnt, q = nxt[p][c]; len[nq] = len[p]+1;
        memcpy(nxt[nq], nxt[q], sizeof nxt[q]); fa[nq] = fa[q];
        fa[q] = fa[np] = nq;
        for (; p && nxt[p][c] == q; nxt[p][c] = nq, p = fa[p]);
    }
}
int main(){
    scanf("%s", A+1); scanf("%s", B+1);
    n = strlen(A+1); m = strlen(B+1);
    root = last = ++cnt;
    rep(i, 1, n) insert(A[i]-'a');

    int ans = 0, nowlen = 0;
    for (int i = 1, p = root; i <= m; i++){
        int c = B[i]-'a';
        if (nxt[p][c]){ nowlen++; p = nxt[p][c]; }
        else{
            for (; p && !nxt[p][c]; p = fa[p]);
            if (!p) p = root, nowlen = 0;
            else nowlen = len[p]+1, p = nxt[p][c]; 
        }
        ckmax(ans, nowlen);
    }

    printf("%d\n", ans);
    return 0;
}

你可能感兴趣的:(后缀自动机)