在上一篇文章字符串匹配算法(一)——BF算法提到过,字符串匹配的思路是固定的:算法
将模式串
和主串
进行比较segmentfault
主串
和模式串
的下一个位置失配时,数组
在模式串
中寻找一个合适的位置数据结构
主串
当前失配位置进行比较模式串
的头部与主串
失配位置的下一个位置进行比较主串
中找到一个合适的位置,从新与模式串
进行比较优化在于其中的步骤,而KMP算法
,就是优化第3步失配时寻找模式串
合适位置的操做。框架
那么如何寻找模式串
中所谓合适的位置呢?能够先来看个栗子:工具
......优化
上面是 BF 匹配过程当中从Nk到Nk+m的 m
次匹配过程,从中咱们能够发现,从第 k
步到第 k+m
步时,指针 i
和 j
又回到了相同的位置,且 第 k+m
步 更具备匹配的可能性,因此咱们思考一下,是否是能够由第 k
步直接跳到第 k+m
步呢?若是能够,就能够减小 m-1
次比较,大大提高效率。再进一步思考,若是将整个匹配过程再看做是重复地由Nk直接到Nk+m的推动,那么每次重复时,模式串
开始比较的位置就是咱们所要找的合适的位置。spa
如何寻找这些位置呢?咱们能够把这个问题转化为求next数组
的过程。3d
咱们再仔细观察下 Nk 和 Nk+m 两个状态指针
因为 Nk 状态下,模式串
与主串
具备彻底匹配的部分,且要达到 Nk+m 状态所需移动到的位置信息也存在于匹配的部分,所以咱们能够无视掉主串
,只看模式串
便可获得next数组
。
再认真观察咱们还能发现,Nk 状态不匹配时,Nk+m 状态本质上是将模式串
中的另一对 AB
和 主串
达成以前的已匹配状态。因此求next数组
的问题又能够转化为当m位置不匹配时,求m位置以前的子串
的最大相同先后缀的问题。
首先要创建一个规则,具备先后缀的字符串长度至少为2,因此咱们定义若是长度为0,则对应next数组
值为-1,若是长度为1,值为0。下面举个栗子:
手工求这么看其实没什么难度,本身多写几个串练一遍就会了。
学会如何手工求next数组
以后,整个KMP算法
的代码如何写呢?
还记得最开始提到要记住的一点吗?匹配思路是同样的,只是优化了失配后的操做。根据这一点,咱们能够把BF算法
的框架先搬过来:
这样是否是能够接下来去补全 getNext()
方法就能够了呢?咱们来看一个特殊状况:
当处在Nk+m状态时,发现失配位置前的 AB
没有最长公共先后缀,因而只能退回到BF算法
的作法,也就是i++;j=0
。可是这和咱们上面的框架代码不符,须要进行改造:
j = next[j] === -1
时,也须要进入第一个分支,使得 i++;j++(-1 + 1 = 0)
,变相达到效果。获得最终的框架代码:
接下来就是进行对next数组
的求解——完善 getNext()
。这时候有的同窗可能就会想对上述手工求法进行代码转化,但是万一模式串
很长的话,那么这个时间复杂度就会变得至关的高,因此须要采用迭代法,利用每次所得的结果来求下一个结果,从而拼凑出next数组
。
咱们假设某一时刻有一个状态Sk
此时咱们已经求完了next[j]
的值,如何去求next[j+1]
呢?仔细观察状态图,发现:
next[j] + 1 = 4
个相同的先后缀 P0P1Pk-jPk 和 Pj-kPj-k+1Pj-1Pj,也就是 next[j+1] = next[j] +1 = k + 1
再来看一个状态
一样是求完了next[j]的值,
若是 Pnextn[k] !== Pj呢?
能够看到,
k = next[k]
直到回到前缀第一个位置,则表示没有相同的先后缀,此时 j = -1
,则 next[j+1] = Pnextn[k] + 1 = k + 1 = 0根据以上分析,咱们能够补充完 getNext()
再优化一下写法
至此,一个完整的KMP算法
就写好了。
咱们来看一个特殊的例子:
这是一个前缀相同的一个模式串
,且咱们已经求得了next数组
,接下来咱们模拟一下上面写好的程序进行的操做:
咱们发现因为前缀都是相等的,当第1步发现失配时,直接 j = -1
就能够了,也就是 next[5] = -1
便可。因此,优化点实际上是体如今对next数组
的优化,咱们称之为nextVal数组
如何求nextVal数组
呢?咱们仍是以上面的特殊状况为例,看两个状态:
此时咱们已经求完了nextVal[j]的值,仔细观察状态图,发现:
根据求next数组
的过程,next[j + 1] = k + 1
能够在 getNext()
的基础上获得如下代码:
next数组
如今就已是一个无关紧要的工具人了,咱们把去掉,获得下一版代码:
再进行如下优化获得最终代码:
总的来讲,KMP算法
和BF算法
的字符串匹配思路在大方向上是没有区别的,只是引入了一个next数组
或nextVal数组
来求得模式串
中合适的位置。只要理解了这两个数组的求法,也就基本理解了KMP算法
。
“字符串匹配算法”是“重学数据结构与算法”系列笔记: