洛谷-P5357-【模板】AC自动机(二次加强版)

题目传送门

-------------------------------------- 过年在家无聊补一下这周做的几道AC自动机的模板题

sol:AC自动机,还是要解决跳fail边产生的重复访问,但是这次用last边已经不行了,只能拿76分。我们把跳fail边的过程放到串扫描完之后一次性进行。

  • AC自动机
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> PII;
    const int MAXN = 200010;
    struct Trie {
        int son[MAXN][26], fail[MAXN];
        int que[MAXN]; int head, tail;
        int cnt[MAXN]; int tot, root;
        int add_node() {
            memset(son[tot], -1, sizeof(son[tot]));
            cnt[tot] = 0;
            return tot ++;
        }
        void init() {
            head = tail = 0;
            tot = 0;
            root = add_node();
        }
        int insert(char* s) {
            int p = root;
            for (int i = 0; s[i]; i++) {
                int index = s[i] - 'a';
                if (son[p][index] == -1)
                    son[p][index] = add_node();
                p = son[p][index];
            }
            return p;
        }
        void build() {
            fail[root] = root;
            for (int i = 0; i < 26; i++) {
                if (son[root][i] == -1) son[root][i] = root;
                else {
                    fail[son[root][i]] = root;
                    que[++ tail] = son[root][i];
                }
            }
            while (head != tail) {
                int p = que[++ head];
                for (int i = 0; i < 26; i++) {
                    if (son[p][i] == -1) son[p][i] = son[fail[p]][i];
                    else {
                        fail[son[p][i]] = son[fail[p]][i];
                        que[++ tail] = son[p][i];
                    }
                }
            }
        }
        void slove(char* s) {
            int p = root;
            for (int i = 0; s[i]; i++) {
                int index = s[i] - 'a';
                p = son[p][index];
                cnt[p] ++;
            }
            for (int i = tail; i >= 1; i--) {
                int p = que[i];
                cnt[fail[p]] += cnt[p];
            }
        }
    } ac;
    int inde[MAXN]; // 本地运行没什么问题,洛谷叫index就报编译错误
    char s[2000010];
    int main() {
        int n; ac.init();
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%s", s);
            inde[i] = ac.insert(s);
        }
        ac.build();
        scanf("%s", s); ac.slove(s);
        for (int i = 1; i <= n; i++)
            printf("%d\n", ac.cnt[inde[i]]);
        return 0;
    }

    经过不断地40分、60分、76分,终于搞定了。为了在最后跳fail边的时候保证深度越高的节点越先跳采用了手写队列保留bfs时候的层次关系。网上看到别人的代码据说是用拓扑的,没细看。不过感觉这里的队列就是一个对深度的拓扑排序了。

posted @ 2020-01-25 19:48  Angel_Demon  阅读(...)  评论(...编辑  收藏