本篇文章主要详细介绍$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. LC 20 Valid Parentheses

    问题 Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if the i ...

  2. 【KMP】Radio Transmission

    问题 L: [KMP]Radio Transmission 题目描述 给你一个字符串,它是由某个字符串不断自我连接形成的.但是这个字符串是不确定的,现在只想知道它的最短长度是多少. 输入 第一行给出字 ...

  3. Python 语言简介与入门

    Python 的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程序,作为ABC语言的一种继承.Py ...

  4. wpf 判断项目中的某个窗体是否已经打开或者已经存在

    foreach (Window item in Application.Current.Windows) {     if (item is window1) return; }

  5. android 自定义控件之NetWorkImageView 处理listview等控件中的图片加载乱序问题

    0.调用: BaseAdapter中设置方法 holder.iv.loadImage(url); adapter_xxx.xml 中 控件需要用 xxx.NetWorkImageView 1 NetW ...

  6. Qt使用自带的windeployqt 查找生成exe 必需的库文件

    集成开发环境 QtCreator 目前生成图形界面程序 exe 大致可以分为两类:Qt Widgets Application  和 Qt Quick Application.下面分别介绍这两类exe ...

  7. 多线程之thread和runnable

    Runnanle方式可以避免Thread由于单继承特性带来的缺陷. Runnable代码可以被多个线程(thread实例)共享,适用于多个线程处理同一资源的情况. 线程的生命周期:创建,就绪,阻塞,运 ...

  8. 关于Mybatis的几件小事(二)

    一.MyBatis缓存机制 1.简介 Mybatis包含了一个非常强大的查询缓存的特性,它可以非常方便地配置和定制. 缓存key极大提高查询效率 MyBatis系统中默认定义了两次缓存 默认情况下,只 ...

  9. vue覆盖UI组件样式不生效

    检查检查是不是加了scoped 在vue中,我们需要引用子组件,包括ui组件(element.iview). 但是在父组件中添加scoped之后,在父组件中书写子组件的样式是无效果的. 去掉scope ...

  10. 使用postman修改SAP Marketing Cloud contact主数据

    Marketing Cloud里的contact主数据,创建成功后也不是所有字段都能够被修改.在Personal data区域的字段是可以被修改的. 比如我在"客户属性"字段里维护 ...