更好的閱讀體驗 Press Here
AC自動機 可食用最佳練手題
題意
有三種操作
* 在字符串的末尾加入小寫字母
* 刪除字符串末尾的字符
* 將當前字符串輸出(不刪除)
問第 個輸出的字符串 在第 個輸出的字符串中出現了幾次
Solution
當然直接暴力是沒有問題的
存下第 個輸出的字符串 然後用 KMP 優化匹配
時間複雜度可以達到 ,仍然會超時
既然需要對多個字符串進行匹配,自然的我們想到 AC自動機 (後綴自動機的泥奏凱)
發現如果第 個輸出的字符串在第 個字符串的 位置匹配,那麼第 個輸出的字符串一定是第 個字符串的 位置的後綴,即可以通過 指針轉移到
那麼原題就轉換爲了在第 個字符串中有多少個字符能夠通過 轉移到第 個字符串的結尾位置
但是直接做時間複雜度仍然很高
我們需要枚舉每個 串,維護一個計數器,從根一路遍歷到 串的末尾節點,途中對於每個節點,如果其 指針指向的是某 串的末尾節點,則累加
這個時間複雜度還是 ,無法通過此題
看到多個點通過 上找一個點,爲何不將其轉化爲一個點通過 找其他點呢?
那麼將 反向,問題變爲 串的結尾位置能夠通過反向的 到達多少屬於 的節點
考慮進一步優化,再這樣一棵由反向 組成的樹上,每個節點所能到達的點一定在它的子樹之中,可以使用 DFS 序維護,同時用樹狀數組求答案
如何統計?
只需要重新按照原來輸入再走一遍,每次遇到加入某個節點,則加入當前節點的 DFS 序;每次刪除某個節點,則刪除當前節點的 DFS 序;每次輸出某個串,說明現在樹狀數組維護的就是這個串所有節點的 DFS 序,那麼就可以統計這個串作爲 串的答案了
詳細代碼如下:
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int m , tot , l[N] , r[N] , t[N] , ans[N];
char s[N];
vector <int> e[N];
vector <pair<int , int>> q[N];
void add(int , int);
int query(int);
struct Aho {
int ch[N][26] , fail[N] , fa[N] , id[N] , go[N];
int cnt , total;
void build() {
int n = strlen(s) , now = 0;
for(int i = 0 ; i < n ; ++ i) {
if(s[i] == 'B') now = fa[now];
else if(s[i] == 'P') {id[++ total] = now; go[now] = total;}
else {
if(!ch[now][s[i] - 'a']) {
ch[now][s[i] - 'a'] = ++ cnt;
fa[cnt] = now;
}
now = ch[now][s[i] - 'a'];
}
}
queue <int> q;
for(int i = 0 ; i < 26 ; ++ i)
if(ch[0][i]) {
q.push(ch[0][i]);
e[0].push_back(ch[0][i]);
}
while(!q.empty()) {
int x = q.front(); q.pop();
for(int i = 0 ; i < 26 ; ++ i) {
if(ch[x][i]) {
q.push(ch[x][i]);
fail[ch[x][i]] = ch[fail[x]][i];
e[ch[fail[x]][i]].push_back(ch[x][i]);
}
else ch[x][i] = ch[fail[x]][i];
}
}
}
void get_ans() {
int n = strlen(s) , now = 0;
for(int i = 0 ; i < n ; ++ i) {
if(s[i] == 'B') {add(l[now] , -1); now = fa[now];}
else if(s[i] == 'P') {
for(auto j = q[go[now]].begin() ; j != q[go[now]].end() ; ++ j)
ans[j -> second] = query(r[id[j -> first]]) - query(l[id[j -> first]] - 1);
}
else {
now = ch[now][s[i] - 'a'];
add(l[now] , 1);
}
}
}
}ac;
int read() {
int ans = 0 , flag = 1;
char ch = getchar();
while(ch > '9' || ch < '0') {if(ch == '-') flag = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {ans = ans * 10 + ch - '0'; ch = getchar();}
return ans * flag;
}
int lowbit(int x) {return x & (-x);}
void add(int x , int y) {if(x) for(int i = x ; i <= tot ; i += lowbit(i)) t[i] += y;}
int query(int x) {int ans = 0; if(x) for(int i = x ; i ; i -= lowbit(i)) ans += t[i]; return ans;}
void dfs(int x) {
l[x] = ++ tot;
for(auto i = e[x].begin() ; i != e[x].end() ; ++ i) dfs(*i);
r[x] = tot;
}
void init() {
scanf("%s" , s);
m = read();
for(int i = 0 ; i < m ; ++ i) {
int x = read() , y = read();
q[y].push_back({x , i});
}
}
void work() {
ac.build();
dfs(0);
}
void print() {for(int i = 0 ; i < m ; ++ i) printf("%d\n" , ans[i]);}
int main() {
init();
work();
ac.get_ans();
print();
return 0;
}