关于KMP算法(多图详解)

为什么用KMP算法

KMP是一种高效的模式匹配的一种算法
模式匹配,说直白就是找寻子字符串在主字符串的位置

(其实KMP这三个字没啥意义,这算法是3个人研究出来的,取3人名字首字母也就是KMP)

暴利匹配算法

与KMP相对立的一个算法就是暴力匹配算法,逐个字符一一对比
形象地理解就是请添加图片描述
可以看到在这个简短的例子中,当主字符串(以下简称主串)对比到3时,与子字符串(简称子串)不同
那么就要折返到开始位置的下一位
如果主串再长点,对比的字符再离奇点(比如刚好和子串最后一位不同步)那么主串折返的次数会更多

假设主串长度为m,子串长度为n,那么主串要比对m-n+1个字符(除了最后那个长度比子串还短的都要比对)
最晦气的情况是刚好每次都是子串的最后一位不同步,那么每个主串字符都要比对n
最坏情况的时间复杂度将是O(m-n+1)n 即 O(mn-n2+n),实际情况是m远大于n,所以可以简写为O(mn)

KMP算法思路

暴力匹配算法最大的弊端就是要折返匹配
在这里插入图片描述
比如在上图这个例子中,明显看到在比对第4位的时候出现的不同步
暴力匹配就会重新从主串第2位开始与子串匹配
显然当前的前3位是同步的,如果错开一位必然不同步,造成浪费时间
但是
已经匹配前3位还是有信息可以用的,当前计算机已经知道了主串前3位的序列,我们直观感受的话,下一次匹配应该是把主串的第3位和子串的第1位开始匹配
在这里插入图片描述
而不必再去和主串第2位比,这个直观感觉的什么原理呢?
对于已经匹配的子串序列,可以取共同元素的下一位作为下一次匹配的起点
看不懂也没关系,请看下面分析

研究子串

(以下示例图,第一个数列是主串截取)
如果是第5位不同步
我们能直观感受到,第4位和第1位是相同元素,可以作为下次匹配的起点
在这里插入图片描述
如果是第4位不同步
可以看到,第3位于第1位有相同元素,可以作为下次匹配的起点
在这里插入图片描述

如果是第3位不同步,那没办法了,前两位都不同,逐一比较了
在这里插入图片描述
如果是第2位不同步,第一位相同没啥用……同上图

如果是第1位不同步,那肯定是没什么好折腾了,老老实实下一个数字比较吧

回顾刚才的思路,可以发现我们是在找寻当前位置之前的字符的共同点
再精确点,当前比对位置是j,前1~j-1个字符组成字符串s
找寻s的最长相等前后缀以实现对已经匹配好的字符的复用

前缀:包含第一个字符但不包含最后一个
后缀:包含最后一个字符但不包含第一个

比如这个子串在这里插入图片描述
当j=6时,1~5组成的字符串是abaab
最长相等前后缀就是ab,当第6位不同步时,直接跳转到第3位
在这里插入图片描述
这样,主串就不必返回到之前的状态,始终都是子串在自行调节对比位序

那么研究一下这个方案:找寻当前比对字符之前的字符,的最长相等前后缀

前方的字符串最长相等前后缀下一个比对位置
abaabab3
abaaa2
abaa2
ab1
a1

可以看到下一个比对位置是最长相等前后缀的长度+1
当比对第一个字符就不同步时,那么主串要向后移一格,我们就记为0,以示意主串后移
那么就得到这么一个数组
在这里插入图片描述

这就是KMP算法中的next数组,用于指示如果当前匹配失败,下次子串比对位置

流程图

在这里插入图片描述

最后一个判断语句,如果是no,那么意味着是i超出了主串长度
也就是这种情况在这里插入图片描述
主串后面无元素了,依然是匹配失败的情况

进一步优化

观察这个子串及其next数组
在这里插入图片描述
可以看到前4位都相同且为a
当第4位不匹配时
在这里插入图片描述
j应该等于next[4]即3
在这里插入图片描述
显然子串的4号位是a,与主串的4号位不同步,那么位于子串3号位的a也必然不同步
同理,当j=2时,也不同步,j继续指向1
那为什么不直接跳转到第一位呢?
所以,在构建好next数组后,还可以进一步优化

对于子串中的相同元素,取位序小的next值作为当前next值
以减少这种不必要的比对
那么新的next数组nextval应该变为
在这里插入图片描述

时间复杂度

主串长度为m,子串长度为n

暴利匹配算法时间复杂度是O(mn)
KMP算法时间复杂度为O(m+n),因为主串没有回退,子串也是近似没有回退
但在一般情况下暴力匹配算法执行时间也近似为O(m+n)毕竟我们寻找的子串大概率是一组无序的字符组成的,而不是有很多重复。
这也是为什么暴力匹配算法沿用至今。其实是懒
请添加图片描述

都看到这里了,留个赞再走呗。

  • 11
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
KMP算法(Knuth-Morris-Pratt算法)是一种用于解决字符串匹配问题的高效算法。它的主要思想是利用匹配失败时的信息,尽量减少比较次数,提高匹配效率。 KMP算法的核心是构建一个部分匹配表(Partial Match Table),也称为Next数组。这个表记录了在匹配失败时应该将模式串向右移动的位置。 构建部分匹配表的过程如下: 1. 首先,将模式串中的第一个字符的Next值设为0,表示当匹配失败时,模式串不需要移动; 2. 然后,从模式串的第二个字符开始,依次计算Next值; 3. 当第i个字符与前面某个字符相同的时候,Next[i]的值为该字符之前(不包括该字符)的相同前缀和后缀的最大长度; 4. 如果不存在相同的前缀和后缀,则Next[i]的值为0。 有了部分匹配表之后,KMP算法的匹配过程如下: 1. 用i和j来分别表示模式串和主串的当前位置; 2. 如果模式串中的字符和主串中的字符相同,那么i和j都向右移动一位; 3. 如果模式串中的字符和主串中的字符不同,那么根据部分匹配表来确定模式串的下一个位置; 4. 假设当前模式串的位置为i,根据部分匹配表中的值Next[i],将模式串向右移动Next[i]个位置; 5. 重复上述步骤,直到找到匹配或者主串遍历完毕。 KMP算法时间复杂度为O(m + n),其中m和n分别是模式串和主串的长度。相比于暴力匹配算法的时间复杂度为O(m * n),KMP算法能够大幅减少比较次数,提高匹配效率。 综上所述,KMP模式匹配算法通过构建部分匹配表并利用匹配失败时的信息,实现了高效的字符串匹配。在实际应用中,KMP算法被广泛地应用于文本编辑、数据搜索和字符串处理等领域。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值