给宝宝的AC自动机启蒙指南(宝宝的第一本)
AC自动机
根据已有经验,学完虚数会变虚,然后写出的代码就不是人能看的了
所以我们来学实树罢(喜)
以上为废话博客背景
有限状态自动机
首先我们来了解一下自动机是啥。
说的通俗一点,我们可以把自动机看成一张有向图,有一个点在起始节点。每当你输入一个合法的东西,这个点就会按照一定规则在边上移动。也就是说,你输入一些东西,这个点就会移动到一个确定的地方是不是很自动呢
我们接触到的第一个自动机其实不是AC自动机,而是trie树。大家可以看一看trie树是不是符合上面这些要求。
当然我是认为这个最大的用处在于让学习AC自动机的人明白trie树很重要。啥?trie树上跑KMP?AC自动机的数组可比KMP好懂多了。相信我。
引入
当然我们是可以按照自动机的结构来讲解的。但那样宝宝就看不懂了。
现在有这样一个问题:有很多模式串和一个文本串,要求有几个模式串在文本串中出现过。
显然,我们可以用模式串建出一棵trie树,一个字母一个字母的跑,跑不到了就重头再来。
但是这很费时间!如果我们能在树上找到失配字符串的后缀,那么失配以后,我们可以直接跳到树上的后缀继续匹配的!:
这是因为,尽管失配了,但我们仍能匹配她的后缀,无需再次重头开始。
AC自动机就是干这个的。
AC自动机的构造
AC自动机,实现的实际上就是多模式串的匹配。我们把这些模式串建成一棵trie树,这是引入里面提到的操作。
我认为,
接下来,我们设数组fail[u]为当在点u失配时可以跳到哪个点接着匹配。
我们肯定希望跳到的点所匹配的长度越大越好,所以我们规定fail[u]为u在trie树中的最长后缀所在节点。
我们来讨论一下怎么求fail。首先显然,与trie树虚拟根节点(这里我设为0)相连的边的fail肯定是0。然后我们根据一个点u的fail去推儿子ch[u][i]的fail,这样的好处是不用记录父亲。
为了表示方便,我们把ch[u][i]设为v
但首先,我们需要判断v存不存在。
- v存在,此时如果fail[u]也存在连向相同字符的边,那么我们就把fail[v]赋值为ch[fail[u]][i](最长后缀和字符串同步加一还是最长后缀)。如果不存在,那我们就需要找到fail[u]的fail,再进行一次判断,因为此时她是有可能存在的最长后缀了。如果还不存在,我们还有再来一次,直到存在或到0。。。好麻烦诶
别担心,我们之后会让这个步骤变得简单。现在我们来看第二种情况:v不存在。此时我们不需要更新v的fail节点,但我们可以想一个问题:当v不存在时,之后跳到u,想连到v,发现v不存在后,还得向上跳。那么,我们为什么不直接把v赋值成上面的某个有这条边的节点or0呢?
我们优化完的策略如下:如果v不存在,那么我们将ch[u][i]设为ch[fail[u]][i],如果存在,不动ch数组,将fail[v]赋值为ch[fail[u]][i]。
为什么这样能达到同样的效果呢?当v存在时,之后连向u的节点,都会把ch赋值为v,直到有另一个更大的后缀存在。当v不存在时,会向fail连,最终会连到上一个存在的节点,或者直接连到0。
大家可以画个图理解成一下,最终会产生这种状况:
ch[u][i] = ch[fail[u]][i] = ch[fail[fail[u]]][i] = ... = w, ch[fail[w]][i] = ch[fail[fail[w]]][i] = ... = 0
上面这种情况不一定准确啊。。。只是想让大家理解一下如果不存在,会直接跳到上一个存在这条边的节点。
或者,如果你的脑回路与我相近,可以尝试思考这个与并查集路径压缩之间的联系。。。
这样子我们就可以直接fail[v]=ch[fail[u]][i]了。因为就算fail[u]并没有这条边,ch[fail[u]][i]也被赋为了最近的一个有这条边的节点。如果都没有,就会干脆赋为0。
由于我们要一层一层的求fail数组,所以我们采用BFS
void build()
{
queue<int> q;
for(int i = 0;i < 26;i ++) if(ch[0][i]) q.push(ch[0][i]);
while(!q.empty())
{
int u = q.front();
q.pop();
for(int i = 0;i < 26;i ++)
{
if(!ch[u][i]) ch[u][i] = ch[fail[u]][i];
else fail[ch[u][i]] = ch[fail[u]][i],q.push(ch[u][i]);//只有存在才能进队
}
}
}
这样还有一个好处,就是我们查询时不需要fail数组了,直接按照trie树的方法查询就行。如果失配就会自动跳到最长的能配对的位置。
AC自动机的查询
这个依题目而定,我们来看几道题目吧
AC自动机模板
给定 \(n\) 个模式串 \(s_i\) 和一个文本串 \(t\),求有多少个不同的模式串在文本串里出现过。
两个模式串不同当且仅当他们编号不同。
对于 \(100\%\) 的数据,保证 \(1 \leq n \leq 10^6\),\(1 \leq |t| \leq 10^6\),\(1 \leq \sum\limits_{i = 1}^n |s_i| \leq 10^6\)。\(s_i, t\) 中仅包含小写字母。
这个是洛谷上面的模板题。
我们根据fail的定义发现,如果我们更新了一个点, 即使她的fail不会被走到,也会作为她的后缀存在于文本串中。也要进行更新。
于是我们搞个标记数组,按照trie树的匹配,每匹配到一个点就跳她的fail,一路跳到vis为1即可
注意这里只是统计出现过,所以我们可以标记到过的点,被标记的点就不再统计
int tiao(string s)
{
int n = s.size(), u = 0, ans = 0;
for(int i = 0;i < n;i ++)
{
int p = s[i] - 'a';
u = ch[u][p];
for(int j = u;j && !ton[j];j = fail[j]) ans += col[j], ton[j] = 1;
// 当一个点被标记过,他的所有的后缀肯定也被标记过,就不用继续跳了
}
return ans;
}
更nb的AC自动机模板
题目描述
有 \(N\) 个由小写字母组成的模式串以及一个文本串 \(T\)。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串 \(T\) 中出现的次数最多。
输入格式
输入含多组数据。保证输入数据不超过 \(50\) 组。
每组数据的第一行为一个正整数 \(N\),表示共有 \(N\) 个模式串,\(1 \leq N \leq 150\)。
接下去 \(N\) 行,每行一个长度小于等于 \(70\) 的模式串。下一行是一个长度小于等于 \(10^6\) 的文本串 \(T\)。保证不存在两个相同的模式串。
输入结束标志为 \(N=0\)。
这道题在洛谷上叫做AC自动机加强版,之后还会有个二次加强版
看起来是让我们求哪些串出现的最多,实际上就是求每个串出现的次数。
于是我们想,我们更新一个节点,她的所有后缀也会被更新。
然后我们把vis扔了,然后跳到一个串,就跳她的fail,一路更新。
看起来很不对,但实际上,每次fail最少也会使深度减少1,所以稍微算算就知道能过
最nb的AC自动机模板
就是上面那题加了数据范围
我们想,我们做第一个模板时,有vis,所以能过,但第二个模板时我们把vis扔了,所以就过不了了
但实际上我们可以从另一个角度优化,第二个模板每一次跳fail都会跳到很多重复的节点,每次+1+1太麻烦,我们攒到最后一起加就行了
AC自动机的应用
AC自动机优化DP
都是套路,如果不是套路,就是套路套套路。
设dp[i]为遍历到AC自动机上节点i时blabla的答案,有时也许要加个限制条件。然后外面套一层循环,里面从节点转移儿子就行
AC自动机的奇怪题目
[POI2000]病毒
题目描述
二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。
示例:
例如如果 \(\{011, 11, 00000\}\) 为病毒代码段,那么一个可能的无限长安全代码就是 \(010101 \ldots\)。如果 \(\{01, 11, 000000\}\) 为病毒代码段,那么就不存在一个无限长的安全代码。
现在给出所有的病毒代码段,判断是否存在无限长的安全代码。
输入格式
第一行包括一个整数 \(n\),表示病毒代码段的数目。
以下的 \(n\) 行每一行都包括一个非空的 \(01\) 字符串,代表一个病毒代码段。
输出格式
如果存在无限长的安全代码,输出 TAK
,否则输出 NIE
。
之前的题目都是要匹配,这道题是不能匹配。而且只要她的fail不合法,她也就不合法
如果走到了走过的地方还没有到非法节点,那么就输出TAK
给宝宝的AC自动机启蒙指南(宝宝的第一本)的更多相关文章
- 浅析 AC 自动机
目录 简述 AC 自动机是什么 AC 自动机有什么用 AC 自动机·初探 AC 自动机·原理分析 AC 自动机·代码实现 AC 自动机·更进一步 第一题 第二题 第三题 从 AC 自动机到 fail ...
- AC自动机讲解+[HDU2222]:Keywords Search(AC自动机)
首先,有这样一道题: 给你一个单词W和一个文章T,问W在T中出现了几次(原题见POJ3461). OK,so easy~ HASH or KMP 轻松解决. 那么还有一道例题: 给定n个长度不超过50 ...
- 【POJ2778】DNA Sequence 【AC自动机,dp,矩阵快速幂】
题意 题目给出m(m<=10)个仅仅由A,T,C,G组成的单词(单词长度不超过10),然后给出一个整数n(n<=2000000000),问你用这四个字母组成一个长度为n的长文本,有多少种组 ...
- LA_3942 LA_4670 从字典树到AC自动机
首先看第一题,一道DP+字典树的题目,具体中文题意和题解见训练指南209页. 初看这题模型还很难想,看过蓝书提示之后发现,这实际上是一个标准DP题目:通过数组来储存后缀节点的出现次数.也就是用一颗字典 ...
- AC自动机详解 (P3808 模板)
AC自动机笔记 0.0 前言 哇,好久之前就看了 KMP 和 Trie 树,但是似乎一直没看懂 AC自动机?? 今天灵光一闪,加上之前看到一些博客和视频,瞬间秒懂啊... 其实这个玩意还是蛮好理解的. ...
- AC自动机--summer-work之我连模板题都做不出
这章对现在的我来说有点难,要是不写点东西,三天后怕是就一无所有了. 但写这个没有营养的blog的目的真的不是做题或提升,只是学习学习代码和理解一些概念. 现在对AC自动机的理解还十分浅薄,这里先贴上目 ...
- 使用AC自动机解决文章匹配多个候选词问题
解决的问题 KMP算法用于单个字符串匹配,AC自动机用于文章中匹配多个候选词. 流程 第一步,先将候选词先建立前缀树. 第二步,以宽度优先遍历的方式把前缀树的每个节点设置fail指针, 头节点的fai ...
- 基于trie树做一个ac自动机
基于trie树做一个ac自动机 #!/usr/bin/python # -*- coding: utf-8 -*- class Node: def __init__(self): self.value ...
- AC自动机-算法详解
What's Aho-Corasick automaton? 一种多模式串匹配算法,该算法在1975年产生于贝尔实验室,是著名的多模式匹配算法之一. 简单的说,KMP用来在一篇文章中匹配一个模式串:但 ...
- python爬虫学习(11) —— 也写个AC自动机
0. 写在前面 本文记录了一个AC自动机的诞生! 之前看过有人用C++写过AC自动机,也有用C#写的,还有一个用nodejs写的.. C# 逆袭--自制日刷千题的AC自动机攻克HDU OJ HDU 自 ...
随机推荐
- 【Java】生成随机字符串
package com.runsky.utils; import java.util.Random; public class GetRandom { private static final Str ...
- pytest与allure的使用
--需要先安装pytest [1]数据驱动@pytest.mark.parametrize: @pytest.mark.parametrize只对于同一用例不同数据的传参 ①只有一个参数时 datat ...
- iOS包大小计算
一.LinkMap文件分析 说明:LinkMap数据是根据文章<LinkMap文件分析>中方法实验实测数据. 如何获得LinkMap文件 1.在XCode中开启编译选项Write Link ...
- sequelize 分页查询
分页原理: 第一步 下面的result 可以修改:
- kali linux|01.kali下安装Nessus
Kali安装Nessus 说明 Nessus是一款基于插件的系统漏洞扫描和分析软件 一.安装 1.下载安装包 https://www.tenable.com/downloads/nessus 查看ka ...
- uniapp 报错 签名不对 请检查签名是否与开放平台上填写的一致
问题描述 用签名工具 输入包名 获取签名 在微信开放平台申请app 用获取的签名申请 申请成功后 在hbuilderx上云打包apk 分享 报 签名不对 请检查签名是否与开放平台上填写的一致 ...
- 【python_PAT_乙类】1013_数素数 ,Python运行超时解决方案
题目: 令 Pi 表示第 i 个素数.现任给两个正整数 M≤N≤104,请输出 PM 到 PN 的所有素数. 输入格式: 输入在一行中给出 M 和 N,其间以空格分隔. 输出格 ...
- c++ 保存txt文件
#include <iostream> #include <stdio.h> #include <fstream> #include <queue> # ...
- Git基础使用和在UE中使用的方法
Git使用介绍 Git使用 1.基础知识 pwd 显示目前的工作目录 print work directory ls 显示当前路径下所有文件 mkdir 产生新的文件夹make directory t ...
- LINUX配置固定IP以及DNS
配置固定ip #vim /etc/sysconfig/network-scripts/ifcfg-ens33 TYPE=EthernetPROXY_METHOD=noneBROWSER_ONLY=no ...