KMP算法及JavaScript实现

本文详细介绍了KMP模式匹配算法的工作原理,包括构建最大相同前缀后缀数组的过程,并给出了JavaScript实现。通过示例解释了如何在文本串中利用该数组进行查找,最后分析了算法的时间和空间复杂度。
摘要由CSDN通过智能技术生成

KMP算法及JavaScript实现

KMP模式匹配算法用于在文本串中快速查找模式串是否存在。
重点就在于构建最大相同前缀后缀数组。

当匹配出现不同时,模式串不是再次从头开始,而是利用前缀后缀相同的特点,从指定位置出发开始匹配。

示例如下:

  1. 在匹配过程中,D与空格不匹配时
    在这里插入图片描述

  2. 普通做法,模式串指针指向起点,重新从头开始匹配:
    在这里插入图片描述

  3. 使用KMP算法,利用前缀后缀的一致性。对于模式串中D的前一位B为止的字符串ABCDAB来说,最大相同前缀后缀长度为2,即前缀中的AB与后缀中的AB是相同的。利用这一特点,因为模式串后缀的AB与文本串中空格前面的的AB是匹配的,所以根据ABCDAB的最大相同前缀后缀长度来移动模式串上的指针如下:

在这里插入图片描述
注意,当不匹配出现时,说明前面部分的字符串是匹配的,所以要访问前面字符串的最大相同前缀后缀长度,不包括不匹配位置,即上例中,是利用ABCDAB的最大相同前缀后缀长度,而不是ABCDABD的。

注意, 这样的移动就是只是移动指针的位置。



思路:

1. 先对模式串进行遍历,建立模式串的最大相同前缀后缀数组。

前缀在这里表示为第一个字符开头,但不包括最后一个字符的字符串。
后缀为最后一个字符结尾,但不包括第一个字符的字符串。

最大相同前缀后缀长度表示前缀后缀相等的最大长度

分析:

  1. 单个字母的情况视为0, 如 ‘a’ 的最大相同前缀后缀长度就是0。
  2. 因为前缀不包括末尾字符,后缀不包括开头字符。'aa’的最大相同前缀后缀长度就是1,即前缀是第一个a,后缀是第二个a。
  3. 要注意不是回文串,比如 ‘aba’ 的最大公共前缀后缀长度也是1,因为ab不等于ba。

这里要对模式串的进行遍历,计算以每个字符为结尾时的最大相同前缀后缀长度,比如abaa,需要以分别求出以第一位的a结尾的最大长度,第2位b结尾的最大长度,第三位a结尾的最大长度,第四位a结尾的最大长度。

下图为一个模式串及其最大相同前缀后缀长度数组的例子:
在这里插入图片描述

这里计算的难点就在于如何快速计算出每个字符对应的最大相同前缀后缀长度,并不是对每个字符进行完全的遍历来求值,而是利用前面字符的最大相同前缀后缀长度来进行计算。


快速计算最大相同前缀后缀长度的方法如下所示:

在这里插入图片描述

对于模式串中的 a 来说,要计算以 a 为结尾的最大相同前缀后缀长度。

  1. 首先需要利用 a 之前一位的最大相同前缀后缀长度,即图中的红色部分;表示这一部分前缀与后缀相同。此时对于前缀区域后面一位b来说,如果b与a相同,则a的最大相同前缀后缀长度就是前一位的长度加1。
  2. 如果不相同,表示无法构成前一位的长度加1的最大相同前缀后缀。就需要利用红色区域即前一位前缀与后缀的相同性,就这个前缀后缀划分为更短的结构。即在相同的前缀后缀长度中寻找更小的相同前缀后缀,如图中黑色区域。

在这里插入图片描述
3. 而为了这种符合的黑色区域需要对红色区域进行分析展开。因为这里并不是回文串,而是最大相同前缀后缀,所以不是红色区域中的任意部分构成的黑色区域都满足相等条件。
即,可以看作在下图这种结构中找出相等的最大相同前缀后缀长度(黑色区域)

在这里插入图片描述

即图中上面表示后缀,下面的表示前缀,即需要找出满足相等条件的黑色区域,因为这里红色表示最大相同前缀后缀区域,因此上下的字符串是相同的,所以在前缀后缀中分别找子最大相同前缀后缀就等同于在一个部分(前缀或后缀)中找子最大相同前缀后缀,如下所示:

在这里插入图片描述

  1. 然后比较黑色区域所代表的最大相同前缀后缀的下一位是否与a相同,

在这里插入图片描述

如果相同,a处的最大相同前缀后缀长度就是黑色区域的长度加1。如果不相同,就对黑色区域进行同样的操作,展开求 子最大相同前缀后缀区域,然后下一位与a比较。直到展开到区域长度为1,即比较首字符与a还不相同时,就赋值0,表示最大相同前缀后缀长度为0。

图解表示如下:

在这里插入图片描述

图中的 i 和next数组可以忽视,思想就是这种一层层展开比较的结构。

实现代码

// 计算next数组 在主函数中移动时取前一位的形式(相当于右移)
function caculateNext(strArr) {
   
    // 先全部赋0
    nextArr = new Array(strArr.length).fill(0);
    let j;
    // 从第一位也就是第二个字符开始比较</
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值