spoj - Longest Common Substring(后缀自动机模板题)

Longest Common Substring

题意

求两个串的最长公共子串。

分析

第一个串建后缀自动机,第二个串在自动机上跑,对于自动机上的结点(状态)而言,它所代表的最大长度为根结点到当前结点的长度,而它的前继结点的串一定是这个结点串的后缀串(或空串)。
匹配过程中一旦失配,自动机上的结点找它的前继结点,继续向后匹配即可。

code

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 250005;
char s[MAXN];

/* 性质:
① 从root到任意结点p的每条路径上的字符组成的字符串,都是当前串t的子串
② 因为满足性质一,所以如果当前结点p是可以接收新后缀的结点,那么从root到任意结点p的每条路径上的字符组成的字符串,都是必定是当前串t的后缀
③ 如果结点p可以接收新的后缀,那么p的fa指向的结点也可以接收后缀,反过来就不行
从 root 出发的任何子串最终都会到达一个合法状态,而非子串最终都会“无路可走”
*/
struct SAM {
    int ch[MAXN << 1][26];
    int fa[MAXN << 1]; // 前继结点,如果当前结点可以接受某个后缀那么它的前继结点也可以接受
    int len[MAXN << 1]; // 从根结点到该结点的最大距离,这个状态(结点)代表的串长度区间 (len[fa], len]
    int cnt, last;
    void init() {
        memset(ch, 0, sizeof ch);
        memset(fa, 0, sizeof fa);
        last = cnt = 1; // root节点为 1 ,所以添加的字符从 2 开始
    }
    void add(int c) {
        int p = last, np = last = ++cnt;
        len[np] = len[p] + 1;
        while(!ch[p][c] && p) {
            ch[p][c] = np;
            p = fa[p];
        }
        if(p == 0) fa[np] = 1;
        else {
            int q = ch[p][c];
            if(len[p] + 1 == len[q]) { // p、q之间无其他结点
                fa[np] = q;
            } else { // np的加入导致前面某个结点的状态发生改变
                int nq = ++cnt;
                len[nq] = len[p] + 1;
                memcpy(ch[nq], ch[q], sizeof ch[q]);
                fa[nq] = fa[q];
                fa[q] = fa[np] = nq;
                while(ch[p][c] == q && p) {
                    ch[p][c] = nq;
                    p = fa[p];
                }
            }
        }
    }
} sam;

int main() {
    scanf("%s", s);
    int len = strlen(s);
    sam.init();
    for(int i = 0; i < len; i++) {
        sam.add(s[i] - 'a');
    }
    scanf("%s", s);
    len = strlen(s);
    int p = 1;
    int ans = 0;
    int c = 0;
    for(int i = 0; i < len; ) {
        while(sam.ch[p][s[i] - 'a']) {
            p = sam.ch[p][s[i] - 'a'];
            i++;
            c++;
        }
        ans = max(ans, c);
        if(!c) i++;
        else {
            p = sam.fa[p];
            c = sam.len[p];
        }
    }
    printf("%d\n", ans);
    return 0;
}
posted @ 2017-08-25 23:48  ftae  阅读(237)  评论(0编辑  收藏  举报