字串專題

zhujio發表於2024-10-01

AC自動機

P2414 [NOI2011] 阿狸的打字機

題意 : 問 AC 自動機中兩串 \(x\)\(y\), \(x\)\(y\) 中出現多少次

對於 \(x\) 串對應節點他會在 \(fail\) 樹中子樹節點出現
那麼問題變成 \(Trie\) 樹中 \(y\) 串對應節點到根節點路徑上有多少個點在 \(x\) 子樹中

注意並不是 \(Trie\) 圖, 我們板子中 AC自動機的 \(nxt\) 陣列 \(build\) 後是 \(Trie\) 圖而不是 \(Trie\) 樹,我們需要開個陣列記錄下建出來的 \(Trie\)

然後感覺是一個很trick的東西

我們把詢問掛在節點上, 對於某個點到根路徑問題
我們可以採用dfs後撤銷當前影響處理

void Dfs(int x) {
    T.add(L[x], 1);
    for (int i = 0; i <= 25; i++) {
        if (Trie[x][i]) Dfs(Trie[x][i]);
    }
    for (auto [y, id] : que[x]) {
        ans[id] = T.sum(L[ref[y]], R[ref[y]]);
    }
    T.add(L[x], -1);
}
點選檢視程式碼
#include <bits/stdc++.h>
using namespace std;
#define endl "\n"

using i64 = long long;

const int N = 2e5 + 100;

vector<array<int, 2>> que[N];

template <typename T>
struct BIT {
    T tr[N];
    int n;
    void init(int n_) {
        n = n_;
        for(int i = 0; i <= n; i++) tr[i] = 0;
    }
    int lowbit(int x) {return x & -x;}
    void add(int x, T v){
        for(int i = x; i <= n; i += lowbit(i)) tr[i] += v;
    }
    T sum(int x) {
        T res = 0;
        for(int i = x; i > 0; i -= lowbit(i)) res += tr[i];
        return res;
    }
    // 查詢最大位置pos,使得1 ~ pos 和小於等於s,a1 + a2 + .... + apos <= s 
    int query(T s){
        int pos = 0;
        for(int i = 20; i >= 0; i--){
            if(pos + (1 << i) <= n && tr[pos + (1 << i)] <= s){
                pos += (1 << i);
                s -= tr[pos];
            }
        }
        return pos;
    }
 
    T sum(int l, int r) {return sum(r) - sum(l - 1);}
    void add(int l, int r, T v) {add(l, v); add(r + 1, -v);}
};

BIT<int> T;

struct Aho_Corasick_Automaton {
    //basic
    int nxt[N][26], fail[N], end[N], len[N];
    int root, tot;

    // g存的fail樹, ref存每個字典串最後一個節點
    vector<int> g[N];

    void clear() {
        memset(nxt[0], 0, sizeof nxt[0]);
        root = tot = 0;
        g[0].clear();
    }
    int newnode() {
        tot++;
        memset(nxt[tot], 0, sizeof nxt[tot]);
        fail[tot] = 0;
        g[tot].clear();
        return tot;
    }
    int Trie[N][26], fa[N];
    map<int, int> ref;
    void init(string str) {
        int now = root, nowid = 0;
        for (int i = 0; i < str.size(); i++) {
            if (str[i] >= 'a' && str[i] <= 'z') {
                int id = str[i] - 'a';
                if (!nxt[now][id]) {
                    nxt[now][id] = newnode();
                    Trie[now][id] = nxt[now][id];
                    fa[tot] = now;
                }
                now = nxt[now][id];
            } else if (str[i] == 'P') {
                ref[++nowid] = now;
            } else {
                now = fa[now];
            }
        }
    }
    void build() {
        queue<int> q;
        for (int i = 0; i < 26; i++) {
            if (nxt[root][i]) q.push(nxt[root][i]);
        }
        while (!q.empty()) {
            int head = q.front(); q.pop();
            for (int i = 0; i < 26; i++) {
                // tire圖
                int tmp = nxt[head][i];
                if(!tmp) nxt[head][i] = nxt[fail[head]][i];
                else{
                    fail[tmp] = nxt[fail[head]][i];
                    q.push(tmp);
                }  
            }
        }
        // 構建fail樹
        for (int i = 1; i <= tot; i++) g[fail[i]].push_back(i);
    }
    int L[N], R[N], dfn;
    void dfs(int x) {
        L[x] = ++dfn;
        for (auto y : g[x]) {
            dfs(y);
        }
        R[x] = dfn;
    }
    int ans[N];
    void Dfs(int x) {
        T.add(L[x], 1);
        for (int i = 0; i <= 25; i++) {
            if (Trie[x][i]) Dfs(Trie[x][i]);
        }
        for (auto [y, id] : que[x]) {
            ans[id] = T.sum(L[ref[y]], R[ref[y]]);
        }
        T.add(L[x], -1);
    }
} ACAM;

void solve() {
    string s;
    cin >> s;

    ACAM.init(s); ACAM.build();
    ACAM.dfs(0); T.init(N - 100);

    int m;
    cin >> m;

    for (int i = 1; i <= m; i++) {
        int x, y;
        cin >> x >> y;
        que[ACAM.ref[y]].push_back({x, i});
    }

    ACAM.Dfs(0);
    
    for (int i = 1; i <= m; i++) {
        cout << ACAM.ans[i] << endl;
    }
}

int main() {
    cin.tie(nullptr) -> ios::sync_with_stdio(false);
    solve();
    return 0;
}

SAM

相關文章