P3808 【模板】AC 自动机(简单版)
P3808
KMP用来求单模式串的匹配,AC自动机用来求多模式串的匹配。
就是给你n个模式串,再给你一个文本串,求有多少个模式串在文本串中出现过。
AC自动机的数据结构基于trie数,像KMP的nxt数组一样维护失配指针fail(寻找fail的过程是用bfs实现的),查询过程中不断向下匹配,匹配不了就往fail指针方向走。
#include <bits/stdc++.h> using namespace std; const int N = 1e6 + 10; struct Trie { int fail;//失配指针 int vis[30];//子节点的位置编号 int End; }AC[N]; int n, tot = 0; string s; void build(string s) { int len = s.length(); int p = 0; for (int i = 0; i < len; i ++) { if (AC[p].vis[s[i] - 'a'] == 0) AC[p].vis[s[i] - 'a'] = ++ tot; p = AC[p].vis[s[i] - 'a']; } AC[p].End ++; } void getfail() { queue<int> q; for (int i = 0; i < 26; i ++) {//处理第二层失配指针 if (AC[0].vis[i] != 0) {//该节点存在 AC[AC[0].vis[i]].fail = 0;//第二层都要指向根节点 q.push(AC[0].vis[i]); } } while (!q.empty()) { int u = q.front(); q.pop(); for (int i = 0; i < 26; i ++) { if (AC[u].vis[i] != 0) {//该节点存在 AC[AC[u].vis[i]].fail = AC[AC[u].fail].vis[i]; //令v = AC[u].vis[i] v是u的儿子,v的fail指向u的fail的相同字母的儿子 q.push(AC[u].vis[i]); } else AC[u].vis[i] = AC[AC[u].fail].vis[i]; //节点不存在就直接将u的儿子节点指向u的fail的相同字母的儿子 } } } int query(string s) {//AC自动机匹配 int len = s.length(); int p = 0, ans = 0; for (int i = 0; i < len; i ++) { p = AC[p].vis[s[i] - 'a']; for (int k = p; k && AC[k].End != -1; k = AC[k].fail) { ans += AC[k].End; AC[k].End = -1;//标记已走过 } } return ans; } int main() { int T; scanf("%d", &T); while (T --) { memset((void *) &AC, 0x00, sizeof(AC)); tot = 0; scanf("%d", &n); for (int i = 1; i <= n; i ++) { cin >> s; build(s); } AC[0].fail = 0;//结束标志(根节点的失配指针一定指向自己) getfail();//求失配指针 cin >> s; cout << query(s) << '\n'; } return 0; }
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现