算法设计与分析——KMP算法

本文详细介绍了KMP算法的思想,包括预处理机制和回溯点的确定,阐述了如何利用最长相同前缀和后缀减少比较次数。KMP算法的预处理过程用于生成局部匹配信息,其核心是next()函数。算法效率分析显示,KMP的时间复杂度为O(m+n)。最后,文章提供了C语言实现的代码示例。
摘要由CSDN通过智能技术生成

前言

上篇文章中介绍的Horpool算法与KMP算法,同属于字符串匹配算法,并且同样利用了预处理的方式,只是二者预处理的方法不同罢了,但根本上都是为了减少不必要的比较过程。相比于Horpool算法,KMP算法更为的高效。

一、算法思想分析

KMP算法存在一个预处理过程,并且这个预处理同样是针对模式串的。那么,既然KMP算法和Horpool算法都存在预处理,我们首先就要理解,这个预处理的机制是如何的?为什么这样进行预处理?
KMP算法思想较复杂,分析过程篇幅较长,请做好思想准备。
KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。而其具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息
KMP算法,利用的是最长的相同的前缀和后缀,以此来构造回溯点,从而减少字符串比较过程。如何利用最长的相同前缀和后缀?
我们用模式串abcdaabcab模拟一下过程,假设当前我们正在将模式串下标为7的字符串c与文本中某个字符比较,而其前面的比较都是匹配的,但是当前字符是不匹配的(就是这个c和文本中对应的比较字符无法匹配)。那么,这个时候,我们就要开始回溯模式串了,就是要回到上一个比较点。可是我要如何确实上一个回溯点,才能保证我比较的次数最少呢?
按照蛮力法的思想,我们就要会到第一个字符了(这里也就是指的a),然后重新比较。可是,我们前面都比较了那么多次了,我们是不是可以利用前面的结果呢?既然我已经匹配到字符c(下标7)了,说明我前面的所有字符都已经匹配成功,而我只需要往前查找一个最长的与前面的子字符串相同的字符串不就可以减少我的比较次数了?这里指的就是前缀后后缀,即在匹配失败的前面的字符串中寻找一个最长的相同的前缀与后缀。那为什么需要这样呢?想象一下,按照蛮力法,我应该是回到第一个字符重新比较的,可是如果我前面已经可以确认第一个字符,甚至是后面很多字符,都是肯定匹配的(因为我已经在c前面匹配过了的!),那么我还需要回到第一个字符么?我直接回到我匹配的最长前缀的后面一个字符即可,然后去继续比较,直到我匹配成功!
这个局部匹配信息是怎样的匹配信息呢?我们来举个栗子来讲解:
假设模式串为 abcdaabcab ,那么利用KMP算法生成的预处理结果(局部匹配信息)如下图。
abcdaabcab的预处理信息
对照着这个图,我们来讲解该预处理结果如何生成的

  • 首先定next[0]的值为-1,这个是一个初始化过程。这个初始化的结果,我们后面会用到的。
  • 依次遍历模式串,从第一个(下标为0)开始,而我们要定义一个用来回溯的标记k,且初始值为-1(其实就是next[0])具体作用,讲解后就知晓。再初始化一个标记j,其功能是用来记录当前遍历的位置。
  • 比较模式串下标为k的字符和模式串下标为j的字符是否相同,在这里可能大家就有个疑问了,k的初始值为-1,数组会越界的吧?直接比较自然会越界,但在比较这两个字符前,我们还有一个条件需要确认,那就是k的值是否为-1(也就是next[0]),如果为-1,那么接下来也就不用比较了,对吧?
  • 所以总结来说,条件就是k==-1或(模式串下标为k的字符和模式串下标为j的字符相同),如果满足这个条件,那么我们就将k加1,j加1,然后赋next[j]=k。这里其实是一个不好理解的点,简单来说,如果当前k为-1,说明当前又回溯到第一个字符了,我们就需要将j位置字符的回溯点设置为第一个字符。而如果两个字符比较成功,那么我们就将j位置字符的回溯点设置为k+1。这里有些不好理解,主要现在我是在讲解预处理结果的生成过程,稍后会
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值