AC自動機

Review: Trie、KMP

偷圖

偷圖 again

Failure function \(\to\) Failure pointer

在KMP中,\(\textrm{fail}_i\)代表的是

「假如在\(i\)這個位置失配的話應該跳到哪裡」

相同地,在AC自動機中,\(\textrm{fail}_u\)代表的是

「假如在\(u\)這個節點失配的話應該跳到哪裡」

\(\Rightarrow \textrm{fail}_u\) 會是\(u\)的一個後綴,並且越長越好,但是必須出現在trie上而且不可以和\(u\)相同

有人喜歡叫他suffix link之類的(?)

小畫家時間

自己畫畫看

實作細節

  • 怎麼建 fail pointer? (top-down / bottom-up)
  • 餵字串進去的時候,中途走到某個節點代表的意義
  • Full automaton

應用

  • AC自動機通常是離線演算法,因為得把小字串全部先丟進去
  • 在AC自動機上DP(?)
  • Failure pointer會形成一棵有根樹 \(\Rightarrow\) 樹上動態求和

CODE (ver. pointer)

#include <bits/stdc++.h>
#define safe cerr<<__PRETTY_FUNCTION__<<" line "<<__LINE__<<" safe\n"

using namespace std;
const int K = 26;

struct AhoCorasick {
    struct node {
        node *ch[K], *fail;
        int cnt;
        node() : ch{}, fail(nullptr), cnt(0) {}
    } *root;
    vector<node*> bfs;
    AhoCorasick() : root(new node()), bfs{root} {}
    node* insert(const string &s) {
        node *now = root;
        for(char c: s) {
            c -= 'a';
            if(!now->ch[c])
                now->ch[c] = new node();
            now = now->ch[c];
        }
        return now;
    }
    void buildFail() {
        queue<node*> q;
        bfs = vector<node*>{root};
        for(int c = 0; c < K; c++) {
            if(root->ch[c])
               root->ch[c]->fail = root, q.push(root->ch[c]);
        }
        while(!q.empty()) {
            node *now = q.front(); q.pop();
            bfs.push_back(now);
            for(int c = 0; c < K; c++) {
                if(now->ch[c]) {
                    node *f = now->fail;
                    while(f != root && !f->ch[c]) f = f->fail;
                    now->ch[c]->fail = f->ch[c] ? f->ch[c] : root;
                    q.push(now->ch[c]);
                }
            }
        }
    }
    void run(const string &s) {
        node *now = root;
        for(char c: s) {
            c -= 'a';
            while(now != root && !now->ch[c]) now = now->fail;
            now = now->ch[c] ? now->ch[c] : root;
            now->cnt += 1;
        }
    }
    void DP() {
        for(int i = int(bfs.size())-1; i >= 0; i--) {
            node *x = bfs[i];
            if(x->fail) x->fail->cnt += x->cnt;
        }
    }
    void clear() {
        for(node *p: bfs) delete p;
        bfs.clear();
        root = new node();
    }
} AC;
signed main() {
    int t;
    cin >> t;
    while(t--) {
        int q;
        string T;
        cin >> T >> q;
        vector<AhoCorasick::node*> ed;
        AC.clear();
        while(q--) {
            string P;
            cin >> P;
            ed.push_back(AC.insert(P));
        }
        AC.buildFail();
        AC.run(T);
        AC.DP();
        for(auto p: ed) cout << p->cnt << '\n';
    }
}

這份是1306的code,不過會MLE

得用陣列型而且要開大概4e5左右才會AC

主要只是放來理解

Problems(?)

Ref

Made with Slides.com