KMP数组!启动!

🤨 一个人能走多远不取决于在顺境时能走多远,而取决于在逆境时多久能找回曾经的自己 — KMP

📝 KMP数组

我习惯于把KMP算法中用于判断匹配过的字符串的Next数组(部分匹配表)称之为KMP数组,今天我们具体来看看KMP数组的实现过程 → 最长公共前后缀长度

KMP算法的基本思想

  1. 利用已有的匹配信息
    • 当在主字符串中进行匹配时,如果遇到不匹配的字符,KMP算法不会简单地将模式字符串向右移动一位重新开始匹配。而是利用之前已经匹配过的部分,根据部分匹配表(前缀函数)的信息,确定下一个可能的匹配位置,从而减少比较次数。
  2. 前缀函数(部分匹配表)
    • 前缀函数用于记录模式字符串中每个位置之前(不包括当前位置)的最长相等的真前缀和真后缀的长度。
    • 通过前缀函数,KMP算法能够知道在发生不匹配时,模式字符串应该向右移动多少位,以便继续进行有效的匹配。

🤗 实现过程

  • 先看看代码吧
def kmp_arr(s: str) -> list[int]:
		"""
		:input   :    传入一个字符串
		:fuction :    求出模式串的部分匹配表
		:return  :    返回匹配表
		"""
    n = len(s)
    kmp = [0] * (n + 1)
    s = '0' + s
    pos = 0 # pos 表示当前字符串的最长公共前后缀长度
    for i in range(2,n+1):
        while pos and s[pos+1] != s[i]: pos = kmp[pos] # 最难懂的一部分
        if s[pos+1] == s[i]:
            pos += 1
            kmp[i] = pos
    return kmp
  1. 初始化
    • pos = 0:表示当前没有任何前缀和后缀匹配。
    • 通常,kmp 数组会初始化为全零数组,表示所有位置初始的最长前缀后缀长度为0。
  2. 遍历模式串
    • for i in range(2, n + 1):从模式串的第2个字符开始遍历到第n个字符。这里假设字符串索引从1开始。
    • 每次迭代,i 表示当前正在处理的字符位置。
  3. 处理当前位置的字符
    • 比较当前字符与前缀字符
      • s[pos + 1]:当前前缀的下一个字符,即尝试扩展前缀。
      • s[i]:当前处理的位置字符。
  4. 处理不匹配情况
    • while pos and s[pos + 1] != s[i]
      • pos > 0 且当前字符不匹配前缀的下一个字符时,需要回退到前缀的最长前缀后缀位置,避免重复比较。
      • pos = kmp[pos]:将 pos 更新为前缀的最长前缀后缀长度,即跳转到 kmp[pos]
  5. 处理匹配情况
    • if s[pos + 1] == s[i]
      • 如果当前字符与前缀的下一个字符匹配,则可以扩展当前的前缀后缀长度。
      • pos += 1:增加前缀后缀的长度。
      • kmp[i] = pos:记录当前 i 位置的最长前缀后缀长度。

四、示例解析

让我们通过一个具体的示例来理解这段代码的工作原理。

示例模式串s = "ABABCABAB"

假设字符串索引从1开始:

索引123456789
字符ABABCABAB

部分匹配表预期结果

i123456789
π[i]001201234

构建过程

  1. 初始化
    • pos = 0
    • kmp = [0] * (n + 1)kmp = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
  2. i = 2
    • 比较 s[1] (A) 和 s[2] (B) → 不匹配。
    • pos = 0
    • kmp[2] = 0
  3. i = 3
    • 比较 s[1] (A) 和 s[3] (A) → 匹配。
    • pos = 0 + 1 = 1
    • kmp[3] = 1
  4. i = 4
    • 比较 s[2] (B) 和 s[4] (B) → 匹配。
    • pos = 1 + 1 = 2
    • kmp[4] = 2
  5. i = 5
    • 比较 s[3] (A) 和 s[5] © → 不匹配。
    • 回退:pos = kmp[2] = 0
    • 比较 s[1] (A) 和 s[5] © → 不匹配。
    • kmp[5] = 0
  6. i = 6
    • 比较 s[1] (A) 和 s[6] (A) → 匹配。 → s[1]回到开头了
    • pos = 0 + 1 = 1
    • kmp[6] = 1
  7. i = 7
    • 比较 s[2] (B) 和 s[7] (B) → 匹配。
    • pos = 1 + 1 = 2
    • kmp[7] = 2
  8. i = 8
    • 比较 s[3] (A) 和 s[8] (A) → 匹配。
    • pos = 2 + 1 = 3
    • kmp[8] = 3
  9. i = 9
    • 比较 s[4] (B) 和 s[9] (B) → 匹配。
    • pos = 3 + 1 = 4
    • kmp[9] = 4

最终,kmp 数组为 [0, 0, 0, 1, 2, 0, 1, 2, 3, 4],这正是我们预期的部分匹配表。


具体的我做了一个图:(美术功底有限)
在这里插入图片描述

📎 参考文章

https://oi-wiki.org/string/kmp/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值