• spoj


    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
    ", ans);
        return 0;
    }
    
  • 相关阅读:
    锁表
    vs2010宏的应用
    Oracle:sqlplus查询出的中文是乱码问题的解决(转)
    linux下查看端口和服务的一些命令
    netsh
    xpshutdown关机命令详解
    linux系统如何察看、修改系统时间
    delphi编程中调用其他执行程序?(转)
    如何修改Oracle用户密码的诀窍(转)
    转:用Delphi实现从Excel数据Update(Insert类似)到Oracle数据库表中
  • 原文地址:https://www.cnblogs.com/ftae/p/7430097.html
Copyright © 2020-2023  润新知