「解题报告」CF1738H Palindrome Addicts

发布时间 2023-06-15 20:21:46作者: APJifengc

神秘回文串题。

首先容易发现要求的是区间本质不同回文串个数,所以直接上论文做法即可。

容易想到增量构建回文自动机,假如现在建出了 \([1, r]\) 的 PAM,考虑有多少回文串出现在了 \([l, r]\) 内。考虑记录每个回文串的最后一次出现位置 \(last_p\),那么这个串的左端点就是 \(last_p - len_p + 1\),也就是需要满足 \(last_p \ge l + len_p - 1\) 那么这个回文串就会造成贡献。

考虑每次插入一个字符时,找到当前的最长的回文串节点后,我们要更新的 \(last_p\) 实际上就是当前节点到根的每一个节点。直接维护好像不是很好维护,我们先观察下性质。根据 PAM 的构造过程容易发现,每拓展一次之后最多只增加一个本质不同的回文串,同理,删除一个字符之后也就最多减少一个本质不同回文串,那么我们其实只需要知道被删除的这一个回文串是哪个就行了。而又容易发现,这个回文串一定满足左端点 \(=l\),且没有任何以它为前缀的更长的回文串(否则这个串一定出现不止一次),也就是在 \(fail\) 树上它是叶子。我们每次删除只删除一个叶子,那么修改的时候实际上也就不需要修改整条链了,只需要给链底打个标记,当这个节点成为叶子被删除后再将标记往上传。整个过程是均摊 \(O(n)\) 的。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1000006;
int n;
char s[MAXN];
char op[20];
int ans;
struct PalindromeAutomaton {
    int t[MAXN][26], fail[MAXN], len[MAXN];
    int tag[MAXN], deg[MAXN];
    vector<int> e[MAXN];
    int tot, now;
    PalindromeAutomaton() {
        fail[0] = fail[1] = 1, len[1] = -1;
        tot = 1, now = 0;
    }
    void insert(int c, int pos) {
        int p = now;
        while (s[pos - len[p] - 1] != s[pos]) p = fail[p];
        if (!t[p][c]) {
            int np = ++tot;
            int q = fail[p];
            while (s[pos - len[q] - 1] != s[pos]) q = fail[q];
            fail[np] = t[q][c], len[np] = len[p] + 2, t[p][c] = np;
        }
        now = t[p][c];
    }
    void pushTag(int p, int v) {
        if (p > 1) {
            if (tag[p] == 0) ans++, deg[fail[p]]++;
            if (tag[p] < v) tag[p] = v, e[v - len[p] + 1].push_back(p);
        }
    }
    int jump(int p, int l) {
        while (len[p] > l) p = fail[p];
        return p;
    }
} pam;
int main() {
    scanf("%d", &n);
    int l = 1, r = 0;
    while (n--) {
        scanf("%s", op);
        if (op[1] == 'u') {
            scanf(" %c", &s[++r]);
            pam.insert(s[r] - 'a', r);
            pam.now = pam.jump(pam.now, r - l + 1);
            pam.pushTag(pam.now, r);
        } else {
            for (int p : pam.e[l]) {
                if (pam.tag[p] == l + pam.len[p] - 1 && pam.deg[p] == 0) {
                    pam.pushTag(pam.fail[p], pam.tag[p]);
                    pam.deg[pam.fail[p]]--, ans--, pam.tag[p] = 0;
                }
            }
            l++;
        }
        printf("%d\n", ans);
    }
    return 0;
}