1. 程式人生 > >洛谷P3966 [TJOI2013]單詞 單詞 (ac自動機 fail樹的應用)

洛谷P3966 [TJOI2013]單詞 單詞 (ac自動機 fail樹的應用)

emp c++ lse class 是我 文章 amp 就是 http

目錄

  • 洛谷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;
}

洛谷P3966 [TJOI2013]單詞 單詞 (ac自動機 fail樹的應用)