相关文章
KMP与AC自动机详细讲解(带图)
2024-11-10 18:49

KMP​ 算法可以说是我学过的算法里最让我印象深刻的一个算法了。初学 KMP​​ 的时候真的是抓耳挠腮,硬啃了一下午的博客才勉强可以自己独立推一遍算法的整个流程。第二次学习 KMP​ 是为了在数据结构课上给同学们介绍这个算法,自己学和教会别人又是不一样的难度,于是我又重新学习了一遍,但这一次学习时有很多之前觉得很抽象的东西都突然茅塞顿开了,为了讲解的效果,我还反复推导了几次算法,确保讲课的流畅。第三次学习 KMP​ 是为了给集训队的学弟们讲这个算法,而竞赛更偏重于算法的应用,所以我在重新推演了一次算法后又找了一些经典例题。自此,对于 KMP 的理解可以说是挺明晰了。最近,我又学习了 AC自动机,很巧的是,AC自动机的思想和 KMP 是一样的,于是我又“被迫”重温了一遍 KMP ,既然那么有缘分,不如就写篇博客吧。

KMP与AC自动机详细讲解(带图)

KMP算法(又称看毛片算法),名字取自它的三个共同发明者名字的首字母,是一个高效率的字符串匹配算法。主要解决这样一个问题:有一个 S 串(称为匹配串)和一个 P 串(称为模式串 pattern​),问在 S 中是否出现过 P 以及出现过几次的问题。

首先我们可以想到一个不计效率的暴力做法:将 S 串的 i​ 位置作为起点与 P 串进行比较,如果整个字符串匹配了则退出,如果在某个位置失配了,则 Si+1 开始作为起点与整个 P 串重新比较,直到 S 串的每个位置都与 P 比较过后结束,假设 S 串长度为 nP 串长度为 m ,则整体的时间复杂度为 O(nm),上述流程的代码如下:

上面整个暴力做法效率太低了,我们考虑如何进行优化?我们在模拟暴力的匹配过程中可以发下,每次失配后 i 指针和 j​​​ 指针都要回退很大的一段长度,然后重新比较,我们如果可以根据已有的信息通过理论分析来减少回退的距离那么就能提高匹配的效率了, KMP 算法就是这样去考虑的。其实 KMP​ 本质就是一个设计精妙的动态规划。

假设我们某次匹配失败了,如下图所示,绿色部分表示匹配成功的部分,在 “x” 处匹配失败。假如 P 串的某个前缀子串和后缀子串相同(红色部分),那么我们就可以利用这个信息减少回退。

由于绿色部分是相同的,很容易可知,S 串对应的红色部分也和 P 串的两个红色部分相同,我们可以将 S 不动,P串向后移动直到 S 的红色部分和 P​ 串的前半个红色对齐,然后继续比较下一个位置是否相同即可,如下图所示:

既然如此,那么我们如果可以找到 P​ 串最长的红色部分,那么我们就可以让 P​​​ 串移动的距离最少使得效率最大化。下面证明一下此结论的正确性:假设某次匹配失败后(图中绿色部分),P串向后移动了一段最短的距离,并成功匹配,如图中红色部分。由于红色部分一一对应相等,必然可得蓝色部分也相等,我们可以发现在 S​ 串中,蓝色部分是绿色部分的后缀,在 P​ 串中蓝色部分是绿色部分的前缀,那么要让 P 串移动的距离最小,其实就是让蓝色部分尽量长,即让绿色部分的前缀和后缀相同且尽量长。

好了,现在我们已经找到优化匹配过程的方法了,就剩下一个问题:如何求得 P​ 串的某个前缀子串的最长的相同的前缀和后缀的长度(有点拗口,多读几遍理解)呢?这就是 KMP 的精妙之处,也是初学者最难理解的部分:next 数组。

2.3.1 什么是next数组?

因为无法知道 P​​​ 的那些子串需要用到最长的相同的前缀后缀长度,所以最稳妥的方法就是预处理出P串的每个前缀子串的最长的相同的前缀后缀长度。于是引出 next​​ 数组的定义:next[i]​​ 表示 P​​ 串下标为 **0​ 到 i-1​​ **的子串 x​​ 的某个前缀与 x​ 的后缀相同,记录最长的长度(注意记录的是0到 i-1 的子串!!),另外这里的前缀和后缀必须都是非平凡的(即不包括自身),比如abcd的前缀只有a,ab,abc,后缀只有d,cd,bcd,后面描述中的前缀后缀都是非平凡的,不再重复。

举个例子:

2.3.2 怎么求next数组?

明确next数组的定义后,思考如何求解next数组。

如图,假设计算 next[i+1] 时某个时候,发现 p[i] neq p[j] ,我们就要考虑有没有更小的一个前缀可以和后缀匹配,假设子串 p[0…j-1]​ 中存在一个最长的前缀和后缀相等(蓝色部分),那么由于绿色部分相等,我们可以推得,绿色中也有一部分和前缀的蓝色相同,那么我们只要比较p[k]p[i]​ 是否相等,如果不相等继续找 k 之前子串的最长相等前缀后缀,直到发现相等或者 k 到边界0,这样就能求得 next[i+1]​ 。

我们发现 j​​​ 指针的转移依据是下标为0到j-1的子串的最长前缀后缀,即通过 j = next[j]​​ 不断向前移动 j​ 指针,直到发生匹配记录相应的 next​ 值,或者 j = 0​ 时也失配则结束寻找相应的 next = 0​,由于要用到之前的 next​ 信息,所以我们可以用递推的方式从小往大求得所有的next值,注意边界 next[0] = -1 ,这是特殊规定的,但也有它的巧妙之处,先给出代码:

代码可能有点地方不太容易理解,主要就是下标的变换,这也是我初学 KMP​ 时最头疼的一个地方,下面对主要部分进行分析,但最好还是手动模拟逐一理解:

现在我们有 next​ 数组了,那么我们再回头将 KMP​​ 代码化吧!如果忘记了怎么用 next 优化匹配的话,可以往前翻重新回顾一下。下面直接给出 KMP​ 的核心代码:

我们可以发现, KMP​ 的写法几乎和 next​ 的求解一模一样,这里就不做过多分析,讲解一下大致流程:首先 ij 都指向起点 0 ,进入循环,如果两者匹配,则同时+1,继续匹配下一个字符,如果发现失配,那么就将 j 指向 next[j] ,同时由于下标从0开始,所以指向的也正好是下一个待匹配的位置,继续匹配,如果一直失配则会遇到边界 next[0] = -1​ ,则将i+1,j+1 ,即i进入下一个位置和 j = 0 比较。循环的边界是 ij 走到字符串外面,如果 j​ 走到字符串外面了,说明 S 串中存在匹配。

分析代码,我们可以发现匹配的过程中 i​ 是不会往回退的,j​ 虽然会回退,但也是有限的几次可以忽略,所以这里的复杂度为 O(n),另外还有求 next 数组,同理可得复杂度为 O(m),故总体复杂度为 O(n+m)

KMP将一个乘法级别的复杂度降低到了加法级别,可以说是巨大的提升,那么是否还有可以优化的地方呢?

观察下面这个情况:

我们发现 s[3] neq p[3]​ ,则令 j = next[3] = 1​ ,即比较 s[3]​ 和 p[1]​ 这必然是失配的,因为一开始 s[3] neq p[3] = ‘b’​,而

p[1]​​​ 也等于‘b’,所以必然失配,对于这种可以推得的“必然失配”的情况,我们都可以将其优化掉。考虑什么时候会出现这的情况:只有当p[j] = p[next[j]]​ 时才会出现上述的情况,那么当 p[j] = p[next[j]] 是,我们只要让 next[j] = next[next[j]] 就行了,这部分的优化可以在 next 数组的求解中完成,优化后的代码如下:

AC自动机顾名思义就是自动AC的机器,可以帮助你将难题直接Accept掉,有些东西还真不能顾名思义,AC自动机全称为Aho-Corasick automaton,该算法在1975年产生于贝尔实验室,是著名的多模匹配算法。所谓多模匹配算法,最常见的例子是给出n个单词,再给出一段包含m个字符的文章,让你找出有多少个单词在文章里出现过。要学习AC自动机,首先需要先学会 Trie(字典树),没了解过的可以先去了解一下,很简单的一种数据结构,下文都建立在掌握字典树的基础上展开。AC自动机算法和KMP很像,一共分为三步:构造一棵 Trie 树,构造失败指针和模式匹配过程。

假设现在有四个单词:、 、 、 ,我们可得下面这样的一颗 Trie树:

我们定义一个 fail​ 指针:对于某个节点 p​ ,它的 fail​ 指针指向某个节点 q​​​ ,我们令:

q​ 节点满足,S​​ 的最长的非平凡后缀(即不包括自身的后缀)与 P​​ 串相等,如果不存在这样一个点 q ,则 fail 指向根节点,据此我们可以画出上图的字典树中每个节点的 fail​ 指针:

上面是 fail​ 指针的抽象定义,其主要功能就是在匹配串匹配失败后,将指针转移到 fail​ 指针指向的地方,这样就不用回溯,而可以一路匹配下去了。其实 fail​ 指针指向的就是当前搜索的串的后缀可以匹配的所有以根节点为起点的子串的前缀的最大值,假设我们有一个匹配串 S​ 在匹配的过程中的某个位置发生失配了,那么以失配位置为结尾的这段字符串的一部分有可能成为某个单词(匹配串)的前缀,所以我们跳到最大的前缀继续比较。

了解了 fail​​​ 指针的定义,我们进行文本串的匹配,假设有个匹配串 ashex,我们沿着字段树向下搜索(蓝色箭头为路径):

一直沿着字典树向下走,直到发现走到一个绿色节点,说明找到了某个单词(字典树中如果某个节点为某个单词的最后一个字符,则会标记这个节点,图中以绿色为标记),此时 ‘h’ 没有后续节点,匹配失败,我们通过最左边的这个 ‘h’ 的 fail​ 指针,转移到了中间的这个 ‘h’ ,然后继续向下走,走到 ‘x’ 后又发现绿色节点,则又找到一个单词。此时’x’没有后续节点,匹配失败,则通过 fail​​ 指针转移,走到 root 节点,匹配结束,整个过程发现两个单词。

以 AcWing 1282. 搜索关键词 为例,给出模板:

由于这道题收录在付费课程中,可能无法查看题目,这里直接给出题目(强推Acwing的算法系列课程,真的很棒!):

1282.搜索关键词

给定 n 个长度不超过 50 的由小写英文字母组成的单词,以及一篇长为 m 的文章。

请问,有多少个单词在文章中出现了。

输入格式

第一行包含整数 T,表示共有 T 组测试数据。

对于每组数据,第一行一个整数 n,接下去 n 行表示 n 个单词,最后一行输入一个字符串,表示文章。

输出格式

对于每组数据,输出一个占一行的整数,表示有多少个单词在文章中出现。

数据范围

1≤n≤10^4,

1≤m≤10^6

输入样例:
输出样例:

代码

可以类比 KMP 其实也是用递推的思想,即使用上面几层的节点求下面的 fail​​ 指针,由于按层求,所以用bfs遍历,具体看代码吧,解释起来有点麻烦,代码详解先鸽着,有空的话再补充。

KMP 一样,fail​ 指针也有相应的优化,在计算 fail 指针时这样进行:

继续鸽,有空再解释

(v_JULY_v大佬的超详细博客,第一次接触kmp就啃的他的博客)

(y老师的模板,十分简短)

    以上就是本篇文章【KMP与AC自动机详细讲解(带图)】的全部内容了,欢迎阅览 ! 文章地址:http://fswenzheng.xhstdz.com/news/6381.html 
     栏目首页      相关文章      动态      同类文章      热门文章      网站地图      返回首页 物流园资讯移动站 http://fswenzheng.xhstdz.com/mobile/ , 查看更多   
最新文章
适合中老年游戏活动的项目有哪些?
引言:为何中老年游戏活动尤为重要 随着社会的不断发展和生活水平的提升,中老年人的生活方式也随之改变。对于他们来说,健康和快乐成为了生活的重要组成部分。而游戏活动,不仅能够增加社交互动,还能锻炼身体与思维。因此,选择适合中老
上海旅游攻略:探访繁华之都的风情韵味
引言:开启上海的奇妙之旅 上海,这座迷人的城市,拥有着深厚的文化底蕴与现代化的繁华景象,无论是初次造访还是再次归来,都会让人惊叹于它独特的风情韵味。在这里,历史与现代交融,传统与创新并存,等待着你去探索这座繁华之都的每个角
高新企业网站优化方法大揭秘!
高新企业网站的重要性 随着互联网的快速发展,企业网站已经成为企业宣传、推广和营销的重要渠道。对于高新技术企业来说,网站更是展示企业形象、产品技术、行业影响力的窗口。因此,如何优化企业网站,提升网站的曝光率和用户体验成为了高
探索旅游景区的独特魅力:人文、自然与体验的完美结合”
引言:旅游景区的魅力所在 在如今快节奏的生活中,越来越多的人选择通过旅行来放松身心,寻找内心的宁静。在旅游的过程中,景区的选择则显得尤为重要。一个优质的旅游景区不仅仅是壮丽的自然风光,还有深厚的人文底蕴和丰富的体验活动。本
提升健康与活力:探索运动健身的多样化内容与方法
引言:健身的时代已来临 随着人们生活水平的提高,越来越多的人开始关注自身的健康与活力。运动健身不再是一种单一的方式,而是发展出了多样化的内容与方法。无论是为了减肥、塑形,还是增强体质,运动健身都成为了许多人的日常习惯和生活
80岁老人旅游规定的常见问题及注意事项解析
引言:老年人的旅游热潮 随着社会的发展和生活水平的提升,越来越多的老年人开始积极参与到旅游活动中。他们用实际行动证明,年龄并不是旅途的限制,反而是丰富人生经验的体现。虽然老年游客在旅途中享有更多的自由和乐趣,但在旅游规定及
AI写作论文是否会被检测?解密检测机制!
引言:AI写作的崛起 近年来,人工智能(AI)技术的发展迅猛,尤其是在写作领域。AI写作工具不仅能生成高质量的文章,还能满足不同用户的需求,成为内容创作的得力助手。然而,伴随着AI写作的普及,一个新的问题也逐渐显现出来:AI写作论文
几月份去兰州旅游最宜?
探索兰州的四季魅力 兰州,作为甘肃省的省会,坐落于黄河之畔,是一座历史悠久的城市,兼具独特的自然风貌与深厚的人文底蕴。每个季节,兰州展现出不同的面貌,吸引着四面八方的游客前来探索。那到底几月份去兰州旅游最为宜人呢?接下来,
探索中国旅游标志的原型与文化内涵的深度解读
探索中国旅游标志的原型与文化内涵 中国作为一个拥有悠久历史和丰富文化的国家,其旅游标志更是象征着一种独特的文化内涵。中国旅游标志的原型多取材于中国传统艺术元素,加之对中国文化的理解与诠释,形成了独具魅力的形象。 中国国徽与中
轻松搞定!服务器配置RAID:提高性能数据安全双保险!
轻松搞定!服务器配置RAID:提高性能数据安全双保险! 随着信息技术的不断发展,服务器在企业中扮演着至关重要的角色。为了提高性能和数据安全,服务器配置RAID已经成为了一种常见的选择。RAID(Redundant Array of Independent Disks)即
相关文章