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;
}