本篇文章主要详细介绍$AC$自动机的$fail$指针:

如果有什么不完善的地方,请联系我$qwq$


前置知识:

1、建议学一下$kmp$算法

2、$Trie$


导入:

AC自动机是用来解决多模板匹配问题的,但是,如果就单纯的把每个模板串拼接在一起,或者单个单个匹配的话,肯定是会超时的,而它的思想是把所有的模式串建立一颗$Trie$,然后用文本串来匹配,那么我们就必须在这颗$Trie$树上进行快速跳转来优化,于是,AC自动机就诞生了


重点:fail指针到底是什么?

我们先来思考一个问题,假如我们按照上面的思想,把每一个模式串建立一颗字典树,那么怎样才能在这个模板串失配后快速跳到下一个有可能成功匹配的字符串来匹配呢?我们举个例子:假设文本串是$bcde$,模式串有两个,分别为$bce$和$cd$,我们为两个模式串建一颗$Trie$,如下图:

我们拿着文本串开始匹配,发现匹配到$2$节点后,就失配了,也就是模式串$bce$失配,那么我们如何快速跳到下一个模式串$cd$来匹配呢?这个时候你可能会说,到$0$节点开始匹配呀,可是这样其实就是重头匹配下一个字符串了,效率并不高,在这里,我们其实可以直接跳到$4$号节点开始匹配,为什么?那是因为我们发现$2$号节点是成功匹配了的,只是不能到$3$号节点去,但是我们发现$2$号节点匹配说明了字符$c$在文本串出现过,那么$c$必定会匹配,所以$0$到$4$节点是怎么样都会匹配的,多举些例子我们会发现,当某个模式串失配后,当另一个模式串的某个前缀等于失配模式串的后缀时,可以得到另一个模式串的前缀肯定会匹配,可以直接跳到前缀继续匹配,也就是下图:

所以,构建$fail$指针其实就是在找最长的与当前失配的模式串后缀相同的与下一个模式串的前缀,那么为什么要找相同的前后缀上面已经说了,就是因为要利用已知信息来加速匹配,那么为什么要最长呢?

其实最长的话就可以保证每个有可能匹配的模式串都匹配到,假设当前节点模式串失配了,就跳最大的前缀,如果又失配,就又到最大前缀的最大前缀,直到没有为止,这样就可以保证全部考虑到。


算法实现(强势图解$qwq$):

先插入代码供下面边模拟边理解

void s_insert(string ss,int order)//构建普通Trie
{
int now=0;
for(int i=0;i<ss.length();i++)
{
if(!ch[now][ss[i]-'a'])
ch[now][ss[i]-'a']=++cnt;
now=ch[now][ss[i]-'a'];
}
num[now]=order;
}
void s_build()//求fail指针
{
queue<int> dui;
for(int i=0;i<26;i++)
if(ch[0][i])
dui.push(ch[0][i]);
while(!dui.empty())
{
int now=dui.front();
dui.pop();
for(int i=0;i<26;i++)
if(ch[now][i])
{
fail[ch[now][i]]=ch[fail[now]][i];
dui.push(ch[now][i]);
}
else
ch[now][i]=ch[fail[now]][i];
}
}

  

下面的图片引用了某大佬的图片,

假设文本串为$ushersheishis$,模式串为$i$,$he$,$his$,$she$,$hers$,黄色结点表示当前的结点u,绿色结点表示已经$BFS$遍历完毕的结点,红/橙色的边表示$fail$指针。

$2$号节点的$fail$指针画错了,$fail[2]=0$.

我们重点分析结点$6$的$fail$指针构建:

  • 找到$6$的父节点$5$,$5$的$fail$指针指向$10$,然而$10$结点没有字母$'s'$连出的边;

  • 所以跳到$10$的$fail$指针指向的结点$0$,发现$0$结点有字母$'s'$连出的边,指向$7$结点;

  • 所以$fail[6]=7$.

另外,在构建$fail$指针的同时,我们也对$Trie$中模式串的结尾构建$fail$指针。这样在匹配到结尾后能自动跳转到下一个匹配项。具体见代码实现。


深入优化:

一般我们建立$AC$自动机都不会单纯的建立字典树,而是建立。。$emm$。。大概是。。字典图的样子吧

我们重点分析一下结点$5$遍历时的情况:

  • 显然,本来应该跳$2$次才能找到$7$号结点,但是我们通过$10$号结点的黑色边直接通过字母$s$找到了$7$号结点。

  • 因此,就能在$O(1)$的时间内对单个结点构造$fail$指针。

这就是$build$完成的两件事:构建$fail$指针和建立字典图。这个字典图也会在查询的时候起到关键作用。

匹配:


总结:

到此,你已经理解了整个$AC$自动机的内容。我们一句话总结$AC$自动机的运行原理: 构建字典图实现自动跳转,构建失配指针实现多模式匹配。

弱势图解AC自动机的更多相关文章

  1. 【集训第二天·翻水的老师】--ac自动机+splay树

    今天是第二天集训.(其实已经是第三天了,只是昨天并没有机会来写总结,现在补上) 上午大家心情都很愉快,因为老师讲了splay树和ac自动机. 但到了下午,我们的教练竟然跑出去耍了(excuse me? ...

  2. hdu 5955 Guessing the Dice Roll 【AC自动机+高斯消元】

    hdu 5955 Guessing the Dice Roll [AC自动机+高斯消元] 题意:给出 n≤10 个长为 L≤10 的串,每次丢一个骰子,先出现的串赢,问获胜概率. 题解:裸的AC自动机 ...

  3. AC自动机:Tire树+KMP

    简介 AC自动机是一个多模式匹配算法,在模式匹配领域被广泛应用,举一个经典的例子,违禁词查找并替换为***.AC自动机其实是Trie树和KMP 算法的结合,首先将多模式串建立一个Tire树,然后结合K ...

  4. 基于trie树做一个ac自动机

    基于trie树做一个ac自动机 #!/usr/bin/python # -*- coding: utf-8 -*- class Node: def __init__(self): self.value ...

  5. AC自动机-算法详解

    What's Aho-Corasick automaton? 一种多模式串匹配算法,该算法在1975年产生于贝尔实验室,是著名的多模式匹配算法之一. 简单的说,KMP用来在一篇文章中匹配一个模式串:但 ...

  6. python爬虫学习(11) —— 也写个AC自动机

    0. 写在前面 本文记录了一个AC自动机的诞生! 之前看过有人用C++写过AC自动机,也有用C#写的,还有一个用nodejs写的.. C# 逆袭--自制日刷千题的AC自动机攻克HDU OJ HDU 自 ...

  7. BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]

    2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2545  Solved: 1419[Submit][Sta ...

  8. BZOJ 3172: [Tjoi2013]单词 [AC自动机 Fail树]

    3172: [Tjoi2013]单词 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 3198  Solved: 1532[Submit][Status ...

  9. BZOJ 1212: [HNOI2004]L语言 [AC自动机 DP]

    1212: [HNOI2004]L语言 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1367  Solved: 598[Submit][Status ...

随机推荐

  1. bzoj2152 聪聪可可 (树形dp)

    大意: 给定树, 随机选两点, 求两点距离是3的倍数的概率. 树形dp入门水题, 枚举每个点作为lca时的答案即可. #include <iostream> #include <qu ...

  2. 怎样修改一个已存在的Cookie

    Cookie的修改也需要借助 Response-Header 的 Set-Cookie 字段, 不过需要注意的是: 待修改cookie的 key / domain / path / secure 必须 ...

  3. [Vue]method与计算属性computed、侦听器watch与计算属性computed的区别

    一.方法method与计算属性computed的区别 方法method:每当触发重新渲染时,调用方法method将总会再次执行函数: 计算属性computed:计算属性computed是基于它们的响应 ...

  4. Java并发与多线程教程(2)

    Java同步块 Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java同步块用来避免竞争.本文介绍以下内容: Java同步关键字(synchronzied) ...

  5. Nopcommerce 项目添加插件

    插件是用来扩展nopCommerce功能的.nopCommerce拥有多种类型的插件.例如:支付方式(PayPal),税务机构,送货方式计算方法(UPS, USP, FedEx),小部件(如“在线聊天 ...

  6. 【C++】如何提高Cache的命中率,示例

    参考链接     https://stackoverflow.com/questions/16699247/what-is-a-cache-friendly-code 只是堆积:缓存不友好与缓存友好代 ...

  7. 温度传感器 DS18B20

    1. 实物图 2. 64位(激)光刻只读存储器 开始8位(28H)是产品类型标号,接着的48位是该DS18B20自身的序列号,最后8位是前面56位的循环冗余校验码 光刻ROM的作用是使每一个DS18B ...

  8. 如何使用Marketing Cloud的扩展字段作为搜索条件进行搜索

    需求:我在Marketing Cloud的contact模型上用custom field这个应用创建了一个Extension field,名称为微信ID. 现在客户的需求是使用这个字段作为过滤条件进行 ...

  9. elasticsearch 配置外网访问

    进入  config/ elasticsearch.ym 修改:network.host: 127.0.0.1 或者内网Ip 添加:http.host: 0.0.0.0

  10. zookeeper不停的拒绝client连接

    1 自己重建了Zookeeper集群,但是之前的应用依赖的事务是前一个Zookeeper的集群的,所以无法识别,重启一下应用就好了