AC 自动机
AC 自动机的 fail 指针实质上表示 \(i\) 指向 \(j\) 表示 \(rt \rightarrow j\) 是 \(rt \rightarrow i\) 的一个后缀。
首先可以确定某个点的 fail 深度 \(<\) 它自己。
假定当前所有深度 \(<\) 它自己的点都已经完成了 fail 的构建。
那么我们发现,当前 \(cur \rightarrow son\),若 \(cur\) 的 fail 节点 \(cur'\) 指向一个字符 \(c_{son'}=c_{son}\),那么就匹配上了后缀。
否则类似 KMP 那样往上跳,不断跳 fail,一直保证后缀性质进行匹配。
那么根据上一层必须全部完成 fail 构建,使用 bfs。
对于当前不存在的 \(tr_{cur,c}\)(字符 \(c\))直接把 \(cur\) 的 fail 处指向 \(c\) 的儿子接在 \(cur\) 后即可。
统计答案很简单,类似 KMP 跳 fail 即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n, cnt, nxt[N], val[N], tr[N][26];
inline void ins(string str) {
int len = str.size(), cur = 0;
for (int i = 0; i < len; ++i) {
int id = str[i] - 'a';
if (!tr[cur][id]) tr[cur][id] = ++cnt;
cur = tr[cur][id];
}
return ++val[cur], void();
}
inline void build() {
queue <int> q;
for (int i = 0; i < 26; ++i) if (tr[0][i]) q.push(tr[0][i]);
while (!q.empty()) {
int cur = q.front(); q.pop();
for (int i = 0; i < 26; ++i) {
if (tr[cur][i]) nxt[tr[cur][i]] = tr[nxt[cur]][i], q.push(tr[cur][i]);
else tr[cur][i] = tr[nxt[cur]][i];
}
}
return ;
}
inline int query(string str) {
int len = str.size(), cur = 0, res = 0;
for (int i = 0; i < len; ++i) {
cur = tr[cur][str[i] - 'a'];
for (int to = cur; to && ~val[to]; to = nxt[to])
res += val[to], val[to] = -1;
}
return res;
}
int main() {
ios_base::sync_with_stdio(false); cin.tie(0), cout.tie(0);
cin >> n; string tmp;
for (int i = 1; i <= n; ++i) cin >> tmp, ins(tmp);
build(); cin >> tmp; return cout << query(tmp) << endl, 0;
}
对于要计算贡献的,可以拓扑优化 AC 自动机。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, cnt, ed[N], siz[N], nxt[N], tr[N][26];
vector <int> g[N];
inline void ins(string str, int d) {
int len = str.size(), cur = 0;
for (int i = 0; i < len; ++i) {
int id = str[i] - 'a';
if (!tr[cur][id]) tr[cur][id] = ++cnt;
cur = tr[cur][id];
}
return ed[d] = cur, void();
}
inline void build() {
queue <int> q;
for (int i = 0; i < 26; ++i)
if (tr[0][i]) q.push(tr[0][i]);
while (!q.empty()) {
int cur = q.front(); q.pop();
for (int i = 0; i < 26; ++i) {
if (tr[cur][i]) {
nxt[tr[cur][i]] = tr[nxt[cur]][i];
q.push(tr[cur][i]);
} else tr[cur][i] = tr[nxt[cur]][i];
}
}
return ;
}
inline void calc(string str) {
int len = str.size(), cur = 0;
for (int i = 0; i < len; ++i)
++siz[cur = tr[cur][str[i] - 'a']];
return ;
}
inline void dfs(int cur) {
for (auto to: g[cur]) dfs(to), siz[cur] += siz[to];
return ;
}
int main() {
ios_base::sync_with_stdio(false); cin.tie(0), cout.tie(0);
cin >> n; string str;
for (int i = 1; i <= n; ++i) cin >> str, ins(str, i);
build(); cin >> str; calc(str);
for (int i = 1; i <= cnt; ++i) g[nxt[i]].push_back(i);
dfs(0); for (int i = 1; i <= n; ++i) cout << siz[ed[i]] << endl;
return 0;
}