AC 自动机
AC 自动机的 fail 指针实质上表示 指向 表示 是 的一个后缀。
首先可以确定某个点的 fail 深度 它自己。
假定当前所有深度 它自己的点都已经完成了 fail 的构建。
那么我们发现,当前 ,若 的 fail 节点 指向一个字符 ,那么就匹配上了后缀。
否则类似 KMP 那样往上跳,不断跳 fail,一直保证后缀性质进行匹配。
那么根据上一层必须全部完成 fail 构建,使用 bfs。
对于当前不存在的 (字符 )直接把 的 fail 处指向 的儿子接在 后即可。
统计答案很简单,类似 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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现