字符串匹配——KMP算法

1.什么是KMP算法?

        KMP算法(Knuth-Morris-Pratt Algorithm),是由Donald Knuth、Vaughan Pratt和James H. Morris三位计算机科学家于1970年代共同提出的字符串搜索算法。它是一种改进的字符串匹配算法,主要用于在一个较大的文本串(通常称为“主串”)中查找一个模式串(Pattern)出现的所有位置。

        在朴素的字符串匹配算法中,当模式串与主串部分匹配但最后发生失配时,需要将模式串回溯到起始位置重新开始匹配。而KMP算法通过预处理模式串并构建一个前缀函数或Next数组,记录了模式串中每一个前缀与后缀的最大相同前缀长度,这样在失配时无需回溯整个模式串,而是利用这个已知信息直接移动模式串指针至适当的位置继续匹配,从而极大地提高了匹配效率。

        KMP算法的时间复杂度为O(n + m),其中n是主串的长度,m是模式串的长度。即使在最坏的情况下,也能避免不必要的字符比较,保证了高效的搜索性能。

2.举例说明

假设我们有一个主串 S = "ababcabcabcdabcde",我们要在其中查找模式串 P = "abcabc"

朴素的字符串匹配方法是逐个比较字符:

  1. 从主串开始位置开始,将 P[0] 与 S[0] 进行比较。
  2. 如果相等,则继续比较 P[1] 和 S[1],直到某个位置不匹配(例如 S[3] != P[3])或者完全匹配成功(即 P[m-1] 与 S[n-m+1] 匹配)。
  3. 不匹配时,主串需要回溯一位,然后重新从头开始比较。

而对于KMP算法,首先预处理模式串得到Next数组(也称部分匹配表),如下所示:

  • P = "abcabc"
  • Next数组:[0, 0, 0, 1, 2, 0]

接下来进行搜索:

  1. 将 P[0] 与 S[0] 比较,相等,继续比较下一个。
  2. 当 S[3] != P[3] 发生失配时,由于 Next[3] 的值为1,我们知道不需要回溯整个模式串,而是直接将模式串右移1位,使得 P[1] 对齐到 S[3] 继续比较。
  3. 继续这个过程,最终找到第一个匹配的位置 S[3]

通过KMP算法,我们可以避免不必要的字符回溯和重复比较,从而提高效率。在上述例子中,最终会找到两个匹配的位置,分别是 S[3] 和 S[9]

那么Next数组中的值怎么来的呢?

Next数组(部分匹配表)的构建是KMP算法的核心预处理步骤。它记录了模式串中每个位置之前的子串中,最长相同前后缀长度的信息。

以字符串 "abcabc" 为例来说明如何构建Next数组:

  1. P[0] 为起始字符,没有前缀和后缀,因此 Next[0] = 0
  2. 检查 P[1],其前缀只有空串,后缀为 "b",两者无相同部分,所以 Next[1] = 0
  3. 对于 P[2],它的前缀和后缀分别是 "a" 和 "bc",它们没有相同的字符,所以 Next[2] = 0
  4. 当检查到 P[3] 时,前缀为 "ab",后缀也为 "bc",这时发现有一个共同的前缀 "b",但我们需要找到最长的相同前后缀,所以 Next[3] 的值应该是 "b" 前面那个位置的 Next 数组值,即 Next[1](因为这里没有更长的相同前后缀),所以 Next[3] = 0
  5. 到 P[4] 时,前缀为 "abc",后缀为 "c",没有公共部分,因此 Next[4] = 0
  6. 最后检查 P[5],前缀为 "abca",后缀为 "bc",这里有公共部分 "bc",且是最长相同前后缀,那么 Next[5] 应该指向这个公共部分在原字符串中的位置(而不是它自身的位置),也就是 Next[3],因此 Next[5] = 1

最终得到的Next数组为 [0, 0, 0, 1, 2, 0]

注意:上述例子中,在构造Next数组时忽略了计算过程中可能存在的更长相同前后缀,实际操作时需要确保找到最长相同前后缀长度。正确的Next数组应为 [0, 0, 0, 0, 1, 2],其中对于 P[3] 来说,并没有长度大于0的相同前后缀,所以 Next[3] = 0

所谓的前缀和后缀,

前缀(prefix)是指从字符串的起始位置开始到任意一个位置结束的部分。例如,在字符串 "abcde" 中,前缀包括:

  • 空串 ""
  • "a"
  • "ab"
  • "abc"
  • "abcd"
  • "abcde"

后缀(suffix)则是指从字符串的某个位置开始直到末尾的部分。对于同一字符串 "abcde",其后缀包括:

  • "e"
  • "de"
  • "cde"
  • "bde"
  • "bcde"
  • "abcde"

       在KMP算法构建Next数组的过程中,我们需要找到模式串P每个位置i之前的子串所对应的最长相同前后缀长度。这里的“相同前后缀”指的是同一个子串既是该位置之前的某个子串的后缀,也是它自己的前缀。

       举个例子,对于字符串 "abcaba",在索引为5的位置时,前缀是 "abcaba",后缀是 "ba",它们的公共部分是 "a",所以这个位置的最长相同前后缀长度就是1。而在索引为3的位置处,前缀是 "abcab",后缀是 "ab",它们有相同的 "ab" 子串,因此最长相同前后缀长度为2。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值