字符串匹配KMP算法

之前的字符串匹配使用暴力拆解的办法,使用两个for循环来慢慢匹配,最坏时间复杂度为O(n*m)

而KMP算法利用已经比较过的那一部分字符串,减少比较的次数
在这里插入图片描述

AB是重复出现的内容,而且已经比较过,是在目标字符串中存在的
在这里插入图片描述

再从对应的位置进行开始比较,减少重复比较的次数

KMP算法就是利用已经比较过的内容,有重复的直接移动到重复的位置,没有重复的,直接比较下一个

而字符串位置的移动是根据部分匹配表来实现

部分匹配表的实现
部分匹配表根据匹配的字符串进行拆解

以字符串“ABCDABD”为例:
"A"的前缀和后缀是空集,为0
"AB"的前缀[A]和后缀[B],共有元素的长度为0
"ABC"的前缀[A, AB]和后缀[BC, C],共有元素的长度为0
"ABCD"的前缀[A, AB, ABC]和后缀[BCD, CD, D],共有元素的长度为0
"ABCDA"的前缀[A, AB, ABC, ABCD]和后缀[BCDA, CDA, DA, A],共有元素的长度为1
"ABCDAB"的前缀[A, AB, ABC, ABCD, ABCDA]和后缀[BCDAB, CDAB, DAB, AB, B],共有元素的长度为2
"ABCDABD"的前缀[A, AB, ABC, ABCD, ABCDA, ABCDAB]和后缀[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0
部分匹配表为[0, 0, 0, 0, 1, 2, 0]
在这里插入图片描述

出现不匹配时,已经成功比较的字符串个数为6
部分匹配表第6个为2
匹配字符串向右移动6 - 2 = 4个位置
在这里插入图片描述
移动位数 = 已匹配的字符数 - 对应的部分匹配值

移动后再从当前的位置比较下去

用JavaScript实现KMP算法

function kmp(content, target) {
    
    const length = target.length;
    
    const arr = [];
    // 获取部分匹配值
    for (let i = 0; i < length; i++) {
        const temp = target.slice(0, i + 1);
        if (temp.length === 1) { // 长度为1,没有前缀和后缀
            arr.push(0);
        } else if (temp.length === 2) { // 长度为2,直接比较前后
            if (temp[0] === temp[1]) {
                arr.push(1);
            } else {
                arr.push(0);
            }
        } else { // 多前后缀比较,获取最长的相同长度
            const a1 = [];
            const a2 = [];
            let flag = true;
            for (let j = 0; j < temp.length - 1; j++) {
                a1.push(temp.slice(0, j + 1));
                a2.push(temp.slice(j + 1));
            }
            for (let g = 0; g <= a1.length - 1; g++) {
                if (a1[a1.length - 1 - g] === a2[g]) {
                    arr.push(a2[g].length);
                    flag = false;
                    break;
                }
            }
            if (flag) {
                arr.push(0);
            }
        }
    }
    
    const result = []; // 匹配结果
    
    let i = 0;
    let r = 0;
    
    while (i < content.length) {
    
        if (content.charAt(i) === target.charAt(r)) { // 当前字符相等
            r++;
            i++;
            if (r === target.length) { // 相等字符数量与目标字符串长度相等
                result.push(i - r);
                r = 0;
            }
        } else { // 当前字符不相等
            if (r !== 0) { // 已有比较相等的字符,进行部分匹配移动位置
                const move = arr[r - 1];
                r = r - (r - move);
            } else {
                i++;
            }
        }
    }
    return result;
}

参考连接: http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值