【 算法 】KMP算法

本文详细介绍了KMP算法的工作原理,包括核心思想、next数组的生成及其在字符串匹配过程中的应用。通过构建next数组来确定模式串在不匹配时的回退位置,避免了不必要的回溯,从而提高了字符串匹配的效率。KMP算法的关键在于找到最长公共前后缀,以实现模式串的有效移动。文中还给出了具体的代码示例,展示了如何实现KMP算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

KMP算法

  • KMP 算法可以做到仅仅后移模式串,比较指针不回溯

核心思想

  • 查找主串与模式串的最长公共前后缀
  • 将模式串中前缀的字符移动到后缀字符的位置
  • 继续往后匹配字符。

next数组

  • 前缀: 包含首位字符但不包括末位字符的子串
  • 后缀: 包含末位字符但不包括首位字符的子串
  • 最长公共前后缀
    • 当我们分析的字符串中有多个公共前后缀时,我们要取最长的公共前后缀。
    • 长度要小于错误位置左端子串长度的公共前后缀。
  • next数组
    • 当主串与模式串的某一位字符不匹配时,模式串要退回的位置
    • 记录最长公共前缀的长度

判断最长公共前缀

  • 判断前缀的下一个字符与后缀末尾字符之后的字符进行对比
  • 我们要用公共前缀的下一个字符,与我们公共后缀的下一个字符做比对
  • 如果它们是一样的,那么我们下一段字符的最长公共前后缀就是 当前最长公共前后缀 + 1
  • 定义 j 指向的就是我们公共前缀的末尾字符,定义 i 指向的就是我们公共后缀的末尾字符。
    • 当前最长公共前后缀长度 + 1 = j 所在下标 + 1
	j  : 0  1  2  3  4  5  6  7  8  9  10  11
    p  : A  B  A  B  A  A  A  B  A  B  A   A	

    下标0
        A
        无最长公共前缀
        next[0] = 0
    下标1
        AB
        无最长公共前缀
        next[1] = 0
    下标2
        ABA
        最长公共前缀:A
        next[2] = 1
    下标3
        ABAB
        最长公共前缀:AB
        next[3] = 2
    下标4
        ABABA
        最长公共前缀:ABA
        next[4] = 3
    下标5
        ABABAA
        最长公共前缀:A
        next[5] = 1
    下标6    
        ABABAAA
        最长公共前缀:A
        next[6] = 1
    下标7   
        ABABAAAB
        最长公共前缀:AB
        next[7] = 2
    下标8   
        ABABAAABA
        最长公共前缀:ABA
        next[8] = 3
    下标9  
        ABABAAABAB
        最长公共前缀:ABAB
        next[9] = 4
    下标10
        ABABAAABABA
        最长公共前缀:ABABA
        next[10] = 5
    下标11   
        ABABAAABABAA
        最长公共前缀:ABABAA
        next[11] = 6

prefix前缀数组 / PMT 数组

    [0,0,1,2,3,1,1,2,3,4,5,6]

prefix数组减去 1 就是 next数组

匹配步骤

  • 定义 i j 从下标0开始
  • 循环直到 i 到达我们主串的长度
  • 匹配成功
    • 模式串 中的 j 指向的字符与 原串 中 i 指向的字符相同时
    • i 和 j 就都可以往右挪动一位,往后继续匹配了。
  • 把公共前缀挪动到公共后缀的位置,j = next [ j ]
  • 找主串中的下一个字符与我们模式串的下标 0 的字符进行匹配了,所以这里就是 ++i 即可。
  • 最后判断 j 是否到达模式串最后
    在这里插入图片描述

代码

function KMP(source, str) {
    // 定义数组
    const next = new Array(str.length).fill(0);
    // next数组
    {
        var i = 1, j = 0;
        while (i < str.length - 1) {
            // 如果匹配成功 那么最长公共前缀长度 j + 1
            if (str[i] === str[j]) {
                j++;
                i++;
                next[i] = j;
            }
            // 匹配失败 j 指针回溯到 0 位置
            else {
                if (j > 0) {
                    j = next[j];
                } else {
                    i++;
                }
            }
        }
        // 0号位置不放置字符
        next[0] = -1;
        console.log(next)
    }

    // 主串匹配
    {
        let i = 0,
            j = 0;

        while (i < source.length) {
            if (str[j] === source[i]) {
                ++j, ++i;
            } else {
                if (j > 0) {
                    j = next[j];
                } else {
                    ++i;
                }
            }
            if (j === str.length)
                return true;
        }
        return false;
    }
}

console.log(KMP('ABABABAABABAAABABAA', 'ABABAAABABAA'));
[
   -1, 0, 0, 1, 2,
   3, 1, 1, 2, 3,
   4, 5
]
true

console.log(KMP('hello', 'll'));
[-1, 0]
true

console.log(KMP('HaHaHa', 'XiXiXi'));
[-1, 0, 0, 1, 2, 3]
false
<think>好的,我需要回答用户关于BF算法KMP算法的优缺点及使用场景的问题。首先,我需要回忆这两种算法的基本概念和工作原理。BF算法,即暴力匹配算法,是通过逐个比较主串和子串的字符来实现匹配的,如果发现不匹配,主串就回溯到开始位置的下一个字符,子串则从头开始。这种方法实现简单,但效率较低,尤其是在最坏情况下时间复杂度是O(n*m),其中n和m分别是主串和子串的长度。 接下来是KMP算法,它的核心在于利用部分匹配表(next数组)来避免不必要的回溯。当发生不匹配时,子串不是每次都回到开头,而是根据next数组回退到某个位置,主串不需要回溯。这样可以减少比较次数,提高效率,时间复杂度为O(n+m)。 然后,我需要比较两者的优缺点。BF算法的优点是实现简单,逻辑清晰,适合小规模数据或匹配概率高的场景。缺点是效率低,特别是在处理大规模数据时,性能下降明显。而KMP算法的优点是高效,尤其是在处理有大量重复前缀或后缀的子串时,通过next数组优化了回溯过程。但它的实现相对复杂,需要预处理生成next数组,占用额外的空间。 应用场景方面,BF算法适用于子串较短、主串和子串匹配概率较高的情况,或者对实现复杂度有严格要求的场景。KMP算法则更适合处理大规模文本搜索,尤其是当子串有重复部分时,如DNA序列分析、编辑器中的查找功能等。 我还需要检查用户提供的引用内容,确保回答中的信息准确。引用中的代码示例展示了BF和KMP的实现,特别是KMP中next数组的生成和匹配过程。这有助于确认KMP算法的核心机制确实依赖于预处理步骤。 最后,生成相关问题时要围绕用户的需求,可能包括算法的时间复杂度分析、具体实现细节、优化方法等,确保问题有针对性且能帮助用户深入理解。</think>### BF算法 vs KMP算法 优缺点及使用场景 #### **一、核心差异对比** | **特性** | **BF算法** | **KMP算法** | |----------------|----------------------------------------|-----------------------------------------| | **时间复杂度** | 最坏$O(n \times m)$,平均$O(n+m)$ | 最坏$O(n + m)$,预处理时间$O(m)$[^1] | | **空间复杂度** | $O(1)$ | $O(m)$(需存储next数组)[^1] | | **核心逻辑** | 暴力逐个字符
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值