ac自动机|非自动ac机(当然也有) 笔记+图解
自动ac机
system("poweroff"); // linux
system("shutdown -s -f"); // windows
ac自动机
在计算机科学中,Aho–Corasick算法是由Alfred V. Aho和Margaret J.Corasick 发明的字符串搜索算法,用于在输入的一串字符串中匹配有限组“字典”中的子串 。它与普通字符串匹配的不同点在于同时与所有字典串进行匹配。算法均摊情况下具有近似于线性的时间复杂度,约为字符串的长度加所有匹配的数量。然而由于需要找到所有匹配数,如果每个子串互相匹配(如字典为a,aa,aaa,aaaa,输入的字符串为aaaa),算法的时间复杂度会近似于匹配的二次函数。
反正每次都懒得写解释,直接复制百度百科
比如
kmp应该会吧(不会也没关系),kmp就是在一个字符串中匹配一个子串,那么ac自动机就是在一个字符串中匹配n个字串。
比如字符串为 abcde
,我们要在其中匹配 a
bc
cde
ac
四个字符串,通过朴素方法+kmp优化,复杂度也会随n的增长而增长,但如果有了ac自动机,这一切都会迎刃而解。(挺符合ac观念的_)
Trie树
在学习ac自动机前,先要学习Trie树,是什么东西呢?我们可以想一下,当我们需要储存字符串时,应该怎么储存呢?:
当然是开辟一个数组储存啦:
但如果我们要储存两个字符串,就变成了:
需要的空间翻了一倍,如果我们储存成千上万的字符串,直接 爆炸~~
如果我们使用这个trie树,以树的方式储存,就可以合并为(抱歉,下面都忘记画 d
了……):
瞬间,空间缩小了n倍。但是,我们很快就可以发现,如果我们储存的是 znpdco
和 znp
这两个字符串,就会成为:
我们怎样才能知道这两个字符串分别是 znpdco
和 zn
还是 znpdco
和 znp
还是 znpdco
和 znpd
还是 znpdco
和 znpdc
……呢
所以我们需要定义一个终止符,用红色表示:
很好,现在就很明显知道我们储存的是 znpdco
和 znp
了。
Code
inline void insert(){
int p=0;
for(int i=1;s[i]!='\0';i++){
if(!trie[p][s[i]]){
trie[p][s[i]]=++cnt;
}
p=trie[p][s[i]];
}
tail[p]++;
}
ac自动机
开始进入正题,当我们使用ac自动机时,我们需要想到kmp的一个理念——不回溯,我们在kmp中使用nxt数组防止回溯,那么我们在ac自动机中用fail数组防止回溯。比如:
中,我们如果在匹配 abcd
时出错,我们还可以匹配 bcd
,如果还出错,就匹配 cd
……:
这就是全部的fail指针。
那么如果构建fail指针呢?
构建fail
我们举一个非常有代表性的例子:假设有5个模式串she he say shr her和一个文本串yasherhs,要求在文本串中查找有多少个模式串出现过。
我们先建树:
首先,第一点,我们都知道第一层的s,h
如果就错了,下面的就不用想了,直接回到root根:
接着,我们可以遍历s
的每一个点h,a
,首先是h,我们可以发现h在s的fail指针中有:
我们就可以直接把它的fail指过去:
为什么可以直接指过去
因为我们的父亲节点根据遍历顺序(个人感觉这个遍历顺序和 bfs
差不多,甚至可以直接理解为bfs)肯定已经指定好fail了。我们可以尝试在父节点中的最优fail中找一找我们的失配节点。
欸,那你这时肯定会说了,如果trie树长这样:
直接指过去就会指向:
一个不存在的虚拟节点。
第三种情况
上面介绍了两种情况,作为根节点的子节点fail直接指向root,作为已匹配好的父节点的子节点,有另一个操作方式,可当我们出现上述指向不存在的节点时,应该怎么办呢?
所以我们在遍历子节点时不可以直接遍历子节点了,而是要把所有节点从a到z遍历一遍,对于真实存在的子节点按情况二处理,对于不存在的节点我们可以把它指向 父节点的fail
的 对应子节点
。
有点绕,举个例子:
我们在给 bc
处理时:
除了要处理c
,还应当处理 a,b,d,e,f,g,...
。这里我们举d的例子:
把这个d指向父节点中的最优fail的失配节点。
但是,新的点也是一个虚拟节点呀!!
没关系,我们等到遍历到紫色点上:
可以进行一样的操作,将新点连接到另一个新点:
假如最终这个新点也是一个虚拟节点:
没有关系,因为默认赋值为0,所以最终还是会跳到根节点:
不过注意,这里的跳转指的不是fail节点跳转,因为trie树中这些节点本身就不存在,更不会调用它们的fail。所以我们修改的是它们父亲节点的儿子指针。
综上,我们就有了——
Code
inline void makeFail(){
queue<int> q;//典型bfs写法
for(int i='a';i<='z';i++) if(trie[0][i]) q.push(trie[0][i]);//情况一,根节点的子节点可以直接赋值
while(!q.empty()){
int p=q.front();
q.pop();
for(int i='a';i<='z';i++){
if(trie[p][i]){//情况二,在它的父亲fail中找目标节点
fail[trie[p][i]]=trie[fail[p]][i];
q.push(trie[p][i]);//入队
}
else{
trie[p][i]=trie[fail[p]][i];//情况三,以免虚拟节点的出现
}
}
}
}
query
查询步骤就很简单了,但是我们要注意,为了防止重复遍历加两次,就要定义vis。
剩下就很简单了:
int query(){
int p=0,ans=0;
for(int i=1;s[i]!='\0';i++){
p=trie[p][s[i]];
for(int j=p;vis[j]==false;j=fail[j]){
ans+=tail[j];
vis[j]=true;
}
}
return ans;
}
All Code
#include<cstdio>
#include<queue>
using namespace std;
int n;
char s[1000010];
int trie[1000010]['z'+1];
int tail[1000010];
int fail[1000010];
bool vis[1000010];
int cnt;
inline void insert() {
int p=0;
for(int i=1; s[i]!='\0'; i++) {
if(!trie[p][s[i]]) {
trie[p][s[i]]=++cnt;
}
p=trie[p][s[i]];
}
tail[p]++;
}
void makeFail() {
queue<int> q;
for(int i='a'; i<='z'; i++) if(trie[0][i]) q.push(trie[0][i]);
while(!q.empty()) {
int p=q.front();
q.pop();
for(int i='a'; i<='z'; i++) {
if(trie[p][i]) {
fail[trie[p][i]]=trie[fail[p]][i];
q.push(trie[p][i]);
} else {
trie[p][i]=trie[fail[p]][i];
}
}
}
}
int query() {
int p=0,ans=0;
for(int i=1; s[i]!='\0'; i++) {
p=trie[p][s[i]];
for(int j=p; vis[j]==false; j=fail[j]) {
ans+=tail[j];
vis[j]=true;
}
}
return ans;
}
int main() {
scanf("%d",&n);
for(int i=1; i<=n; i++) {
scanf("%s",s+1);
insert();
}
makeFail();
scanf("%s",s+1);
printf("%d",query());
}
例题
说明一下,我在这里写了一个通知小彩蛋,在电脑端可以开启通知权限试试……QWQ
代码同上
ac自动机|非自动ac机(当然也有) 笔记+图解的更多相关文章
- AC自动机板子题/AC自动机学习笔记!
想知道484每个萌新oier在最初知道AC自动机的时候都会理解为自动AC稽什么的,,,反正我记得我当初刚知道这个东西的时候,我以为是什么神仙东西,,,(好趴虽然确实是个对菜菜灵巧比较难理解的神仙知识点 ...
- 洛谷 P3808 【模板】AC自动机(简单版) 题解
原题链接 前置知识: 字典树.(会 \(\texttt{KMP}\) 就更好) 显然呢,本题用 字典树 和 \(\texttt{KMP}\) 无法解决问题. 所以我们发明了一个东西: \(\textt ...
- AC 自动机学习笔记
虽然 NOIp 原地爆炸了,目前进入 AFO 状态,但感觉省选还是要冲一把,所以现在又来开始颓字符串辣 首先先复习一个很早很早就学过但忘记的算法--自动 AC AC自动机. AC 自动机能够在 \(\ ...
- 给宝宝的AC自动机启蒙指南(宝宝的第一本)
AC自动机 根据已有经验,学完虚数会变虚,然后写出的代码就不是人能看的了 所以我们来学实树罢(喜) 以上为废话博客背景 有限状态自动机 首先我们来了解一下自动机是啥. 说的通俗一点,我们可以把自动机看 ...
- 【原创】AC自动机小结
有了KMP和Trie的基础,就可以学习神奇的AC自动机了.AC自动机其实就是在Trie树上实现KMP,可以完成多模式串的匹配. AC自动机 其实 就是创建了一个状态的转移图,思想很 ...
- POJ2778 DNA Sequence(AC自动机+矩阵快速幂)
题目给m个病毒串,问不包含病毒串的长度n的DNA片段有几个. 感觉这题好神,看了好久的题解. 所有病毒串构造一个AC自动机,这个AC自动机可以看作一张有向图,图上的每个顶点就是Trie树上的结点,每个 ...
- POJ 2778 DNA Sequence (AC自动机,矩阵乘法)
题意:给定n个不能出现的模式串,给定一个长度m,要求长度为m的合法串有多少种. 思路:用AC自动机,利用AC自动机上的节点做矩阵乘法. #include<iostream> #includ ...
- HDU 2222 Keywords Search(AC自动机)题解
题意:给你几个keywords,再给你一段文章,问你keywords出现了几次. 思路:这里就要用到多模匹配算法AC自动机了,AC自动机需要KMP和字典树的知识,匹配时是在字典树上,失配我们就要用到类 ...
- 2017ACM暑期多校联合训练 - Team 8 1006 HDU 6138 Fleet of the Eternal Throne (字符串处理 AC自动机)
题目链接 Problem Description The Eternal Fleet was built many centuries ago before the time of Valkorion ...
- AC自动机学习小结
AC自动机 简要说明 \(AC\) 自动机,全称 \(Aho-Corasick\ automaton\) ,是一种有限状态自动机,应用于多模式串匹配.在 \(OI\) 中通常搭配 \(dp\) 食用. ...
随机推荐
- Stable Diffusion生成图片的参数查看与抹除方法
前几天分享了几张Stable Diffusion生成的艺术二维码,有同学反映不知道怎么查看图片的参数信息,还有的同学问怎么保护自己的图片生成参数不会泄露,这篇文章就来专门分享如何查看和抹除图片的参数. ...
- 如何用Three.js + Blender打造一个web 3D展览馆
作者:vivo 互联网前端团队- Wei Xing 运营活动新玩法层出不穷,web 3D炙手可热,本文将一步步带大家了解如何利用Three.js和Blender来打造一个沉浸式web 3D展览馆. 一 ...
- Harbor 容器镜像仓库
Harbor仓库概述 Docker官⽅提供了Registry镜像仓库,但是Registry的功能相对简陋.Harbor是VMware公司提供的⼀款镜像仓库,提供了权限控制.分布式发布.强⼤的安全扫描与 ...
- java发送http请求(jquery发送http请求,前后端看这一篇文章够了,很完整)
为什么写这篇博客? 1.目前很多系统使用了微服务架构,那么各个微服务之间进行内部通信一般采用http协议的方式,springcloud中提供了ribbon,feign,openFeign等组件. 但是 ...
- 记录一次线上服务CPU飙高问题
2023.07.20 20:01:38线上一个服务发生了CPU过高的告警, 看告警信息当前的CPU使用率已经达到了82.65%,问题已经很严重,赶紧开始排查起来.来复盘下如何排查这类问题, 一.排查方 ...
- QLabel类中的常用方法&信号
setAlignment: 按固定值方式对齐文本 Qt.AlignLeft:水平方向靠左对齐 Qt.AlignRight:水平方向靠右对齐 Qt.AlignCenter:水平方向居中对齐 Qt.Ali ...
- OpenCV实战:从图像处理到深度学习的全面指南
本文深入浅出地探讨了OpenCV库在图像处理和深度学习中的应用.从基本概念和操作,到复杂的图像变换和深度学习模型的使用,文章以详尽的代码和解释,带领大家步入OpenCV的实战世界. 1. OpenCV ...
- Rollup 编译资源离不开 plugin
rollup 也是一个 JavaScript 的模块化编译工具,可以帮助我们处理资源. 与webpack比较 rollup相比 webpack 理念更为简单,能处理的场景也更有限. 资源类型 处理方式 ...
- Datahub稳定版本0.10.4安装指南(独孤风版本)
大家好,我是独孤风,大数据流动的作者. 曾几何时,我在第一次安装JDK环境的时候也遇到了不小的麻烦,当时还有朋友就因为这个环境问题觉得自己根本不是编程的料,选择了放弃.当时有个段子说,"如果 ...
- mybatis-plus+nacos配置中心和服务发现保姆级教程
默认你已经看了我的Mybatis-Plus+Mysql的教程,现在有了一个简单的项目如下(之前的教程: https://www.cnblogs.com/leafstar/p/17638741.htm ...