Educational Codeforces Round 71 G.Indie Album(ac自动机+dfs序线段树维护fail树)

输入一颗字典树,之后m个查询,每个查询给出一个串,问这个串在字典树某一个节点代表的串中出现了多少次。

一个串在另一个串中出现了多少次是sam的基操,但是这里输入了一颗字典树,4e5个节点,于是后缀自动机维护的难度和复杂度就变得巨大,分析了一下,似乎是不可做的。

久违的用了一次ac自动机来解决一个串在另一个串里出现了多少次的问题,只需要建出fail树,这个问题就变成了一个串的链有多少个节点在另一个串的子树里。使用dfs序+树状数组or线段树即可解决。

一开始把查询串也建入了字典树,超时。没办法只能单独把查询串建ac自动机,然后把输入的字典树放到ac自动机上跑(相当于一个取出字典树所代表的串),边跑边把经过的节点+1,然后遇到了需要查询的节点,就查询区间和,最后回溯的时候再把经过的节点-1。

ac代码:

#include <bits/stdc++.h>

using namespace std;

const int maxn = 4e5 + 5;

char s[maxn];

struct AC_Automaton {
    int next[maxn][26];
    vector<pair<int, int>> q[maxn], trie[maxn];
    int ans[maxn];
    int fail[maxn];
    vector<int> tree[maxn];
    int tot, in[maxn], out[maxn];
    int sz, root;

    int newNode() {
        return sz++;
    }

    void init() {
        sz = 0;
        root = newNode();
    }

    int add() {
        int p = root;
        for (int i = 0, c; s[i]; ++i) {
            c = s[i] - 'a';
            if (!next[p][c]) {
                next[p][c] = newNode();
            }
            p = next[p][c];
        }
        return p;
    }

    void getFail() {
        queue<int> q;
        for (int i = 0; i < 26; i++) {
            if (next[root][i]) {
                q.push(next[root][i]);
            } else {
                next[root][i] = root;
            }
        }
        while (!q.empty()) {
            int p = q.front();
            q.pop();

            tree[fail[p]].push_back(p);
            for (int i = 0; i < 26; i++) {
                if (next[p][i]) {
                    fail[next[p][i]] = next[fail[p]][i];
                    q.push(next[p][i]);
                } else {
                    next[p][i] = next[fail[p]][i];
                }
            }
        }
    }


    int t[maxn << 2];

    void update(int rt, int l, int r, int pos, int val) {
        if (l == r) {
            t[rt] += val;
            return;
        }
        int mid = (l + r) >> 1;
        if (pos <= mid) {
            update(rt << 1, l, mid, pos, val);
        } else {
            update(rt << 1 | 1, mid + 1, r, pos, val);
        }
        t[rt] = t[rt << 1] + t[rt << 1 | 1];
    }

    int query(int rt, int l, int r, int L, int R) {
        if (l >= L && r <= R) {
            return t[rt];
        }
        int mid = (l + r) >> 1, ans = 0;
        if (L <= mid) {
            ans += query(rt << 1, l, mid, L, R);
        }
        if (R > mid) {
            ans += query(rt << 1 | 1, mid + 1, r, L, R);
        }
        return ans;
    }

    void dfs(int x) {
        in[x] = ++tot;
        for (auto v : tree[x]) {
            dfs(v);
        }
        out[x] = tot;
    }

    void dfs2(int x, int p) {
        update(1, 1, tot, in[p], 1);
        for (auto e:q[x]) {
            ans[e.second] = query(1, 1, tot, in[e.first], out[e.first]);
        }
        for (auto v:trie[x]) {
            dfs2(v.first, next[p][v.second]);
        }
        update(1, 1, tot, in[p], -1);
    }

    void solve() {
        init();

        int n, m, op, fa;
        char c;

        scanf("%d", &n);
        for (int i = 1; i <= n; ++i) {
            scanf("%d", &op);
            if (op == 1) {
                scanf(" %c", &c);
                trie[0].push_back({i, c - 'a'});
            } else {
                scanf("%d %c", &fa, &c);
                trie[fa].push_back({i, c - 'a'});
            }
        }

        scanf("%d", &m);
        for (int i = 1; i <= m; ++i) {
            scanf("%d %s", &fa, s);
            q[fa].push_back({add(), i});
        }

        getFail();
        dfs(root);
        dfs2(0, root);

        for (int i = 1; i <= m; ++i) {
            printf("%d\n", ans[i]);
        }
    }

} ac;

int main() {
    ac.solve();
    return 0;
}
发布了156 篇原创文章 · 获赞 20 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/Cymbals/article/details/100127736