KMP 学习记录

引言

b站上看到个kmp讲解,讲解得非常清晰,特此做笔记记录下。

KMP (Knuth-Morris-Pratt)算法是一种用于在文本串(主串)中查找子串的高效算法,特别适用于需要频繁查找多个子串的情况。
它的核心思想是利用已经部分匹配的信息,避免在主串中重复检查已经匹配过的字符序列,从而达到减少匹配次数和提高匹配效率的目的。

前缀函数

记π[i] = 第i个前缀的最长匹配真前后缀的长度
π[0] = 0
示例:
s = ATAATA

下标 i012345
字符串 s[i]ATAATA
前缀 s[0…i]AATATAATAAATAATATAATA
π[i](前缀函数值)001123

前缀函数应用

将搜索词称作模式串 s

将正文称作主串 S

思路

为了利用前缀函数(π 数组)高效匹配,我们可以将两者拼接成一个新串:

s + '#' + S

(其中 # 是一个不会在两串中出现的分隔符)

计算这个新串的前缀函数数组 π,
当某个位置 i 满足:

π[i] == s.length()

就说明在主串中,出现了一个与模式串完全匹配的子串。


示例

在主串 AABAATAATA 中查找模式串 ATAATA

模式串 s = "ATAATA"
主串   S = "AABAATAATA"
组合串 str = "ATAATA#AABAATAATA"
步骤 1:计算组合串的 π 数组
下标 i字符前缀π[i]
0AA0
1TAT0
2AATA1
3AATAA1
4TATAAT2
5AATAATA3
6#ATAATA#0
7AATAATA#A1
8AATAATA#AA1
9BATAATA#AAB0
10AATAATA#AABA1
11AATAATA#AABAA1
12TATAATA#AABAAT2
13AATAATA#AABAATA3
14AATAATA#AABAATAA1
15TATAATA#AABAATAAT2
16AATAATA#AABAATAATA6 ✅

步骤 2:匹配结果
  • π[16] == 6 == s.length() 时,说明匹配成功。
  • 位置计算:
    匹配子串起始下标 = 当前下标(16) − 模式串长度(6) − 分隔符(1) − 模式串长度(6) + 1
    → 在主串 S 的第 4 个字符开始匹配

即:

A A B A A T A A T A
      ↑
      从这里开始出现 ATAATA

讨论如何快速求解π[i]

//创建 π 数组,π[i] 表示前缀 s[0..i] 的最长匹配真前后缀长度。
vector<int> pi(str.size());
for(int i = 1; i < str.size(); i++>){
  // 求解 pi[i] 时 我们可以先想 pi[i-1] 的情况。从上一个位置的最长匹配长度开始尝试。
  int len = pi[i - 1];
  //如果当前字符与期望位置不匹配,就回退到更短的“可用前缀”,核心所在
  while(len != 0 && str[i] != str[len]){
    len = pi[len - 1];
  }
  // 如果匹配 字符扩展一位
  if(str[i] == str[len]){
    pi[i] = len + 1;
  }
}

对于每个位置 i,我们尝试在上一个位置 i-1 的最长匹配 pi[i-1] 的基础上延长一个字符;如果延长失败,就利用 pi 数组中的信息不断跳到“较短但仍然是前后缀”的长度上再尝试,直到成功或退到 0。这样避免了重复比较,使整体为线性时间。

时间复杂度分析

虽然看起来 while 可能反复回退,但注意:

  • 每次循环中 len 都会减小;

  • 而匹配成功时 len 增大;

  • 整个过程中 len 增加与减少的总次数 ≤ n。

因此,整体时间复杂度是 O(n),远优于暴力匹配的 O(n²)。

总结

KMP 的核心思想就是“匹配失败时不回退主串指针”,
利用前缀函数 π 数组快速跳转到下一个可匹配的前缀位置。
这样就能保证每个字符只被访问有限次,实现线性匹配。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值