隐藏页面特效

AC自动机

1|0写在前面


本篇代码来源于yyb大佬的博客(指路
加上了一些自己的理解,重写了代码注释,可能算转载plus罢。


2|0代码思路


说到AC自动机,总会提起这个老生常谈的前置知识:Trie+KMP
事实上,它的代码也几乎就是这两者的组合形式。

2|1主体部分:


建Trie树,求失配指针,查询
(即build,get_fail,query三个函数)

  1. 建Trie树:单纯的模板。

  2. 失配指针:祖先失配节点的同字符子节点。
    (简单来说是↑,不过严格来讲,失配指针应当指向“沿着其父节点 的 失配指针,一直向上,直到找到拥有当前这个字母的子节点 的节点 的那个子节点”)

  3. 查询:扫一遍文本串,维护Trie树上的指针,进行统计(见注释)

2|2简化部分:


自定义数据类型
(struct Tree)

2|3注意事项:


输入string类型用cin,方便又快捷(虽然不如char+scanf优雅)
字典树数组开26,则下标从0开始


3|0原理(极简)


Trie+KMP=AC自动机


4|0代码注释


洛谷P3808为例。

#include <bits/stdc++.h> using namespace std; //建Trie树,求失配指针,查询 const int N = 1e6+5; // 数据范围 int n, cnt = 0; // cnt:Trie树大小计数 struct Tree{ int fail; // 失配指针 int vis[26]; // 字典树每个节点(至多)有26个儿子 int end; // 是多少个字符串的末字符 bool fid; // 用于在查询时判重 }t[N]; void build(string s){ // 建Trie树,这里的s是模式串 int l = s.length(); // 模式串长度 int now = 0; // Trie树特有的跳来跳去下标 for(int i = 0; i < l; i++){ // 从零开始的异世界字符串 if(!t[now].vis[s[i]-'a']) // 如果还没有对应字符的子节点 t[now].vis[s[i]-'a'] = ++cnt; // 新建一个节点 now = t[now].vis[s[i]-'a']; // 下移一层 } t[now].end++; // 累计字符串末位字符 } int que[N]; // 队列,用于BFS void get_fail(){ // 求失配指针 int hd = 1, tl = 0; // 队首 队尾 for(int i = 0; i < 26; ++i){ // 枚举树的第一层 if(t[0].vis[i]){ // 如果存在字符'a'+i t[t[0].vis[i]].fail = 0; // 令其失配指针指向根节点 que[++tl] = t[0].vis[i]; // 放入BFS队列 } } while(hd<=tl){ // BFS int u = que[hd++]; // 从队首取一枚节点 for(int i = 0; i < 26; ++i){ // 枚举该节点的儿子 if(t[u].vis[i]){ // 如果存在一个'a'+i的子节点 t[t[u].vis[i]].fail = t[t[u].fail].vis[i]; // 求子节点的失配指针 que[++tl] = t[u].vis[i]; // 放入BFS队列 }else{ // 不存在儿子则 t[u].vis[i] = t[t[u].fail].vis[i]; // 为了后续求失配指针,将儿子指向失配指针对应的儿子。 } } } } int query(string s){ // 查询(这里的s是文本串) int l = s.length(); // 文本串长度 int now = 0, ans = 0; // Trie树指针 统计答案 // 这个模板里面,ans统计的是文本串中 不同编号的模式串 的数量。 for(int i = 0; i < l; i++){ // 枚举文本串 now = t[now].vis[s[i]-'a']; // 指针下移一层(最初在根节点,而根节点无实际意义) for(int j = now; j&&!t[j].fid; j = t[j].fail){ // ans += t[j].end; // 累计数量 t[j].fid = 1; // 标记该模式串已经被统计过 } } return ans; // 返回答案 } int main(){ scanf("%d", &n); // 输入模式串数量 string s; // 一串多用.JPG for(int i = 1; i <= n; i++){ cin >> s; // 输入模式串 build(s); // 维护Trie树 } t[0].fail = 0; get_fail(); // 求失配 cin >> s; // 输入文本串 printf("%d\n", query(s)); // 输出答案 }

__EOF__

本文作者Meteor2008
本文链接https://www.cnblogs.com/meteor2008/p/17730960.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   _kilo-meteor  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· Apache Tomcat RCE漏洞复现(CVE-2025-24813)
点击右上角即可分享
微信分享提示