[kuangbin]后缀自动机题解

[kuangbin]后缀自动机题解

学了后缀自动机,推荐几个比较好的学习资料:

  1. clj老师的NOI现场讲稿PPT,很详细但是不是很好懂
  2. Menci大佬的博客,讲解上更数学(这是好事.jpg),而且还有很好的配图方便理解
  3. 经典俄文教程的翻译,讲解很好懂,不过读起来就有点像外国教材那种废话很多的感觉……

SPOJ - LCS

题意

最长公共子串,字符串长度$ \le 250000$,字符集为小写字母,时限294 ms

解题思路

用后缀数组的话,可以用非法字符连接AB后建出SA,求出LCP最大值即可,复杂度\(O(N)\) (DC3)

不确定能不能过,因为DC3不太好写就不试了,反正是为了练后缀自动机

用SAM的话,可以对A串建出SAM,然后让B串在SAM上运行,如果某次转移\(c\)失配,那么就沿着后缀连接转移,直到当前节点可以匹配\(c\)。在这个过程中维护匹配的子串长度,如果成功转移就\(+1\),沿着后缀连接移动就变成当前节点的\(max\)值(节点表示的字符串的最大长度)

但是这个时限实在是太紧了,而且SPOJ很慢……,需要很优秀的SAM实现才能过这题,我使用Menci的用动态申请点实现SAM的板子怎么都过不了,看来必须静态写法才能过这题,最后在博客上当了一份比较好的SAM实现:

#include <cstdio>
#include <cstring>
#include <algorithm>
using std::max;
using std::min;
using std::copy;
using std::fill;

const int MAX_N = 250009;

struct Node {
    static Node buf[], *bufp;
    void *operator new(size_t) {return bufp++;}

    int lim;
    Node *ch[26], *f;
    Node() = default;
    Node(int lim_): lim(lim_) {
        fill(ch, ch+26, nullptr);
    }
} Node::buf[MAX_N*4], *Node::bufp=buf, *root, *last;

void append(char c) {
    int x = c-'a';
    Node *now=new Node(last->lim+1), *p;
    for (p=last; p && !p->ch[x]; p=p->f)
        p->ch[x] = now;
    if (!p) {
        now->f = root;
    } else {
        Node *q = p->ch[x];
        if (q->lim == p->lim+1) {
            now->f = q;
        } else {
            Node *r = new Node(p->lim+1);
            copy(q->ch, q->ch+26, r->ch);
            r->f = q->f;
            q->f = now->f = r;
            for (; p && p->ch[x]==q; p=p->f)
                p->ch[x] = r;
        }
    }
    last = now;
}

char a[MAX_N], b[MAX_N];

int main() {
    scanf("%s%s", &a, &b);
    last = root = new Node(0);
    for (char *p=a; *p; ++p)
        append(*p);

    Node *now = root;
    int ans = 0, pref = 0;
    for (char *p=b; *p; ++p) {
        int x = *p-'a';
        for (; now && !now->ch[x]; now=now->f)
            ;
        if (!now) {
            now = root;
            pref = 0;
            continue;
        }
        pref = min(pref, now->lim)+1;
        ans = max(ans, pref);
        now = now->ch[x];
    }
    printf("%d\n", ans);
}

用了不少优秀的技巧,比如重载new运算符实现内存池,用指针遍历字符串来少求一次strlen

学习一个.jpg

猜你喜欢

转载自www.cnblogs.com/falseangel/p/9016013.html