js实现kmp算法_js实现KMP算法,浅显易懂

开始

开始看了很多遍视频,一直一脸懵逼,然后看了几篇博客文章,一i边比较,一边自己码,总算理解了。

首先,KMP算法是用来干什么的?用来匹配字符串,如果匹配,返回索引值。

其次,为什么要用KMP算法?因为能简化时间复杂度(废话,算法都是用来提升效率的)。

然后,KMP算法是以什么方式简化时间复杂度的?

一般我们匹配字符串可以用正则表达式,或者拿这个字符串与目标字符串一个个比较,那么就有一个问题,如果其中一个、最坏的是仅仅最后一个不匹配依然要从头开始匹配,这就很没必要,如:

而KMP算法,当某一个匹配失败后,通过一个next数组指导下一次匹配索引。展现出来的效果:

很明显,它没有回去从 b 开始匹配,由于前四个字符都一一匹配,且各不相同,直接跳至 a 和 e 比较,然后马上匹配到正确的字符串。

在这里,模板字符串的前四个字符是各不相同的,所有一次跳四位,如果模板字符串在失配的字符前的字符有相同的呢?就要用到next数组。

next数组

前缀后缀就不说了,只讲最大公共字符串长度

例如一个模板字符串:ababac

为了适应代码和一些字符串方法,我求最大公共字符串长度方式可能有点不一样。比如abab:就假设在后面这个b处失配时,它的前缀后缀最大公共字符串长度为1;比如ababac:就假设在 c 处失配时,它的前缀后缀最大公共字符串长度为3

最后由最大公共字符串长度值,组成数组next数组,最重要的是next数组的用法。

代码区

先看总体代码

const str = 'abababababac';

const mat = 'ababac';

/**

*

* @param {*} str 目标字符串

* @param {*} mat 模板字符串

*/

function kmp(str, mat) {

let commonMaxLen = 0; // 最大公共字符串长度

let next = [0, 0]; // 字符串的前两个字符的最大公共前缀长度直接赋值为0

let matLen = mat.length; // 模板字符串长度

let strIndex = 0; // str 中当前匹配的索引

let matIndex = 0; // mat 中当前匹配的索引

// 该循环求出 next 数组

for (let i = 2; i <= matLen - 1; i++) {

let temp = mat.slice(0, i)

for (let j = 1; j < temp.length - 1; j++) {

let tempSub1 = temp.slice(0, j)

let tempSub2 = temp.slice(temp.length - j, temp.length);

if (tempSub1 == tempSub2) {

commonMaxLen = tempSub1.length;

}

}

next[i] = commonMaxLen;

}

console.log('next: ', next); // [ 0, 0, 0, 1, 2, 3 ]

while (matIndex < matLen && strIndex < str.length) {

if (str[strIndex] == mat[matIndex]) { // 如果当前两个字符相等,移动至下一位

strIndex++;

matIndex++;

} else {

if(matIndex==0){ // 如果指示为元素为 0,目标字符串索引加1,从头开始匹配

strIndex+=1;

}else {

matIndex = next[matIndex]; // next 数组中的元素指示模板字符串哪个元素去匹配

}

}

}

if(matIndex==matLen){ // 匹配成功时,跳出while循环,模板字符串索引是等于它的长度的

return `该字符串位于 ${strIndex-matLen} 处`;

}

return '没找到';

}

next数组产生部分

无论哪个字符串,它的前两个字符的最大公共字符串长度都是0,因此上面直接赋值const next=[ 0, 0]。

利用slice方法,每次截取一个子字符串,然后循环,继续截取,比较是否相等,得到最大长度后,直接赋值给next数组。细节地方就是,按照我的方法看待最大公共字符长度,字符串最后一位是假设它失配,看它前缀后缀匹配个数就好。因此,slice方法不用截取模板字符串的最后一位。

// 该循环求出 next 数组

for (let i = 2; i <= matLen - 1; i++) {

let temp = mat.slice(0, i)

for (let j = 1; j < temp.length - 1; j++) {

let tempSub1 = temp.slice(0, j)

let tempSub2 = temp.slice(temp.length - j, temp.length);

if (tempSub1 == tempSub2) {

commonMaxLen = tempSub1.length;

}

}

next[i] = commonMaxLen;

}

字符串匹配部分

循环结束条件:如果模板字符串索引不小于(一般要么小于要么等于)它自身长度(最后一个字符匹配后,有个加1)结束循环:如果目标字符串的索引不小于它自身长度,结束循环;

判断条件:第一个 if 呢,如果当前两个字符匹配,两个索引都加一,然后再判断连续的下一个字符。else 不匹配时,再分两种情况:如果模板字符串的索引为 0,说明第一个就没有匹配成功;如果模板字符串的索引不为 0 ,说明前几次有匹配成功的(因为有个 ++ ),然后通过next数组中对应的索引元素指示下一次匹配使用的模板字符是哪一个。

while (matIndex < matLen && strIndex < str.length) {

if (str[strIndex] == mat[matIndex]) { // 如果当前两个字符相等,移动至下一位

strIndex++;

matIndex++;

} else {

if(matIndex==0){ // 如果指示为元素为 0,目标字符串索引加1,从头开始匹配

strIndex+=1;

}else {

matIndex = next[matIndex]; // next 数组中的元素指示模板字符串哪个元素去匹配

}

}

}

判断是否匹配成功

如果模板字符串索引值等于它自身的长度,说明是把它自身所有的字符完全匹配成功后才跳出。不然的话,它会被next数组赋值。

if(matIndex==matLen){ // 匹配成功时,跳出while循环,模板字符串索引是等于它的长度的

return `该字符串位于 ${strIndex-matLen} 处`;

}

return '没找到';

标签:字符,匹配,浅显易懂,next,索引,KMP,字符串,js,模板

来源: https://blog.csdn.net/qq_45209973/article/details/104843515

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值