洛谷P3966 [TJOI2013]单词 单词 (ac自动机 fail树的应用)

目录

洛谷P3966 [TJOI2013]单词 单词 (ac自动机 fail树的应用)

题目链接

概述:

ac自动机的fail指针构建成了一颗树。如果fail[p]指向q。那么我们假设根到p的字符串为str, 根到q的字符串为ttr. 那么str的后缀就是ttr的前缀, 某字符串在自动机上出现了。

对于这题, 每个在每个单词插入字典树的时候一路加加,代表这个单词的这个前缀出现过。那么整个自动机就是我们的文章。还有些情况我们需要统计就是单词作为某些后缀出现的情况。cnt[ fail[x] ] += cnt[x] 这样我们按深度由深到浅计算贡献即可(避免重复计算)。同时我们发现整个贡献计算就是队列的逆顺序。所以我存到了一个栈中。

参考代码

ps:自动机风格是看着kuangbin的学的

#include <bits/stdc++.h>

using namespace std;
const int MAXN = 1e6 + 7;

char buf[MAXN];

int pos[MAXN], n;

stack<int>st;

struct ACauto {

    static const int MAXN = 1e6 + 7;

    int fail[MAXN], ch[MAXN][30], cnt[MAXN], tot, root;

    int new_node() {
        for(int i = 0; i < 26; i++ ) {
            ch[tot][i] = -1;
        }
        cnt[tot++] = 0;
        return tot - 1;
    }

    void init() {
        tot = 0;
        root = new_node();
    }

    void insert(char *str, int len, int id) {
        int now = root;
        for(int i = 0; i < len; i++ ) {
            if(ch[now][ str[i] - 'a' ] == -1) {
                ch[now][ str[i] - 'a' ] = new_node();
            }
            now = ch[now][ str[i] - 'a' ];
            cnt[now]++;
        }
        pos[id] = now;
    }

    void get_fail() {
        queue<int>que;
        fail[root] = root;
        for(int i = 0; i < 26; i++ ) {
            if(ch[root][i] == -1) {
                ch[root][i] = root;
            } else {
                fail[ ch[root][i] ] = root;
                que.push(ch[root][i]);
            }
        }
        while(!que.empty()) {
            int now = que.front();
            que.pop();
            st.push(now);
            for(int i = 0; i < 26; i++ ) {
                if(ch[now][i] == -1) {
                    ch[now][i] = ch[ fail[now] ][i];
                } else {
                    fail[ ch[now][i] ] = ch[ fail[now] ][i];
                    que.push(ch[now][i]);
                }
            }
        }
    }

    int query(char str[], int len) {
        int now = root;
        int res = 0;
        for(int i = 0; i < len; i++ ) {
            now = ch[now][ str[i] - 'a' ];
            int pos = now;
            while(pos != root) {
                res += cnt[pos];
                cnt[pos] = 0;
                pos = fail[pos];
            }
        }
        return res;
    }

};

ACauto ac;

int main() {
    scanf("%d", &n);
    ac.init();
    for(int i = 1; i <= n; i++ ) {
        scanf("%s", buf);
        ac.insert(buf, strlen(buf), i);
    }
    ac.get_fail();
    while(!st.empty()) {
        ac.cnt[ ac.fail[st.top()] ] += ac.cnt[ st.top() ];
        st.pop();
    }
    for(int i = 1; i <= n; i++ ) {
        printf("%d\n", ac.cnt[pos[i]]);
    }

    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Q1143316492/p/9568829.html