【数据结构】串的模式匹配:KMP算法(重点)

考点频率 :★★★★★(数据结构必考,选择题常考next数组的计算,下午题偶尔出现)

难度 :⭐⭐⭐⭐⭐(数据结构中最难理解的内容之一)

建议:重点掌握next数组的推导方法,理解KMP如何避免主串指针回溯,会手动计算next数组值

1️⃣ 什么是串的模式匹配?

模式匹配 是指:在主串(目标串) 中查找模式串(子串) 的位置。

  • 主串 :被查找的字符串,如 "ABABCABAB"(可以理解为"长文本")
  • 模式串 :要查找的字符串,如 "ABAB"(可以理解为"关键词")

生活类比 :你在Word里按 Ctrl+F 搜索一个词,Word会在整篇文档(主串)里找这个词(模式串),并告诉你它在第几页第几行------这就是模式匹配。

2️⃣ 朴素匹配(BF算法)的痛点

朴素匹配(Brute-Force,BF) 是最直接的思路:从主串的每个位置开始,逐个字符与模式串比较,匹配失败就回溯到下一个位置重新开始。

复制代码
主串:A B A B C A B A B
模式:A B A B

第1轮:A B A B ✅ 匹配成功!

(这就是为什么它叫"暴力"------它从主串的每个位置都试一遍,直到找到匹配或试完所有位置)

BF的时间复杂度 :O(n×m)O(n \times m)O(n×m),其中 nnn 是主串长度,mmm 是模式串长度。最坏情况下(如主串为 "AAAA...AB",模式串为 "AAAA...AC"),几乎每个位置都要比较 mmm 次。

BF的痛点 :每次匹配失败后,主串的指针要回溯到开始位置的下一个字符,之前比较过的信息全部丢弃,导致大量重复比较。

3️⃣ KMP算法的核心思想

KMP算法 (Knuth-Morris-Pratt)的核心是:匹配失败时,主串指针不回溯,只移动模式串指针

  • 主串指针(i):只前进,不后退
  • 模式串指针(j) :根据 next 数组回退到某个位置,继续匹配

生活类比:你看一本书找一句话。BF算法是:每次发现不对,就把书合上,翻回开头下一页重新看。KMP是:你手里夹着一张书签(next数组),发现不对时,书签告诉你"别翻回去,从书签标记的那一页接着看就行"------你只往前翻,永远不往回翻。

KMP的工作流程

  1. 主串指针 i 从 0 开始,模式串指针 j 从 0 开始
  2. 如果 主串[i] == 模式串[j]i++j++
  3. 如果 j 已经到达模式串末尾 → 匹配成功,返回 i - j
  4. 如果 主串[i] != 模式串[j]
    • 如果 j == 0i++(第一个字符都不匹配,主串指针前移一位)
    • 否则:j = next[j](模式串指针回退到 next[j],主串指针 i 不动)

4️⃣ next数组的含义(核心)

next[j] 表示:当模式串中第 j 个字符与主串不匹配时,模式串指针应该回退到的位置。

next 数组的值仅取决于模式串本身 ,与主串无关。这意味着,对于任意给定的模式串,我们只需要事先算好一个固定的 next 数组,就可以在任何主串中使用它来加速匹配------一次计算,到处使用

核心理解next[j] 是模式串前 j 个字符中,最长相等前后缀的长度。它是一个"真前缀"与"真后缀"的匹配度,表示当匹配失败时,模式串可以"复用"多少已经比较过的信息。

具体含义 :当模式串的 j 位置匹配失败时(即 主串[i] != 模式串[j]),模式串的 j 指针回退到 next[j]继续与主串的当前位置 i 比较 (主串指针 i 不动)。

5️⃣ 如何手工计算 next 数组

5.1 前缀、后缀、最长相等前后缀

  • 前缀:除最后一个字符外,从第一个字符开始的连续子串
  • 后缀:除第一个字符外,到最后一个字符结束的连续子串
  • 最长相等前后缀:前缀集合和后缀集合的交集中,长度最长的那个

示例 :模式串 "ABAB",计算前3个字符 "ABA" 的最长相等前后缀。

  • 前缀:"A""AB"
  • 后缀:"A""BA"
  • 交集:"A"
  • 最长相等前后缀长度 = 1

5.2 手算 next 数组(下标从 0 开始)

设模式串为 P,长度为 mnext[0] = -1(第一个字符不匹配时,主串指针前移)。

对于 j >= 1next[j] = 模式串 P[0..j-1] 中最长相等前后缀的长度。

模式串 j 前 j 个字符 最长相等前后缀长度 nextj
"ABAB" 0 - - -1
1 "A" 0 0
2 "AB" 0 0
3 "ABA" 1("A" 1

所以 next = [-1, 0, 0, 1]

5.3 另一种常见定义(下标从 1 开始,考试可能遇到)

有些教材中,next[1] = 0next[j] = 模式串 P[1..j-1] 中最长相等前后缀长度 + 1。

这种定义下,next 数组的值整体比下标从0开始的定义大1。考试中如果遇到,看清题目给出的定义方式,按照题目给定的定义来计算

6️⃣ KMP算法的匹配过程示例

主串"ABABCABAB"

模式串"ABAB"

next[-1, 0, 0, 1]

复制代码
第1轮:i=0, j=0
主串:A B A B C A B A B
模式:A B A B
       ↑
匹配:A == A ✅ → i=1, j=1

第2轮:i=1, j=1
主串:A B A B C A B A B
模式:A B A B
         ↑
匹配:B == B ✅ → i=2, j=2

第3轮:i=2, j=2
主串:A B A B C A B A B
模式:A B A B
           ↑
匹配:A == A ✅ → i=3, j=3

第4轮:i=3, j=3
主串:A B A B C A B A B
模式:A B A B
             ↑
匹配:B == B ✅ → i=4, j=4

第5轮:j == 4(模式串长度),匹配成功!
匹配位置 = i - j = 4 - 4 = 0(主串中从下标0开始的子串匹配)

实际上KMP的优势主要体现在匹配失败时。上面的例子匹配过程非常顺利,没有触发回退。为了更好地展示KMP的"主串指针不回溯"特点,下面换一个更典型的场景:

场景 :主串 "ABABABC",模式串 "ABABC",匹配到第5个字符时失败。

复制代码
第1-4轮:A B A B 都匹配成功(i=4, j=4)
第5轮:主串[i] = A,模式串[j] = C → 匹配失败!
此时主串指针 i=4(指向'A'),模式串指针 j=4(指向'C')

BF做法:i 回溯到 1,重新从模式串第0位开始比较 → 已比较的信息全丢失
KMP做法:查 next[4] = 1,j = 1,i 不动(仍为4)
模式串指针从1开始继续与主串的 i=4 位置比较

主串指针 i 没有回溯,这就是KMP效率高的根本原因。

7️⃣ KMP vs BF 对比

对比项 BF(朴素匹配) KMP
主串指针 匹配失败时回溯 永不回溯
模式串指针 每次都回到0 根据next数组回退
时间复杂度 O(n×m)O(n \times m)O(n×m) O(n+m)O(n + m)O(n+m)
额外空间 O(m)O(m)O(m)(next数组)
预处理 需要计算next数组

8️⃣ 经典例题

例题1 :模式串 "ABCDABD" 的 next 数组(下标从0开始)为( )。

解析

  • next[0] = -1
  • j=1:前1个字符 "A" → 最长相等前后缀长度 0 → next[1] = 0
  • j=2:前2个字符 "AB" → 最长相等前后缀长度 0 → next[2] = 0
  • j=3:前3个字符 "ABC" → 最长相等前后缀长度 0 → next[3] = 0
  • j=4:前4个字符 "ABCD" → 最长相等前后缀长度 0 → next[4] = 0
  • j=5:前5个字符 "ABCDA" → 前缀 "A" = 后缀 "A" → 长度 1 → next[5] = 1
  • j=6:前6个字符 "ABCDAB" → 前缀 "AB" = 后缀 "AB" → 长度 2 → next[6] = 2

答案[-1, 0, 0, 0, 0, 1, 2]


例题2 :已知模式串 P = "ABABC",next 数组为 [-1, 0, 0, 1, 2]。当匹配到 j=4 时失配,模式串指针应回退到( )。

A. 0

B. 1

C. 2

D. 3

解析next[4] = 2,回退到 2。选 C


例题3(判断):KMP算法中,当匹配失败时,主串指针需要回溯到之前的位置重新比较。( )

解析 :错误。KMP的核心优势就是主串指针永不回溯,只移动模式串指针。

9️⃣ 记忆口诀

KMP核心不回溯,主串指针只前进。

next数组看模式,最长相等前后缀。

next[0] = -1next[j] 算前 j 个字符的最长相等前后缀长度。

匹配失败看 next,模式指针跳过去。

🔟 小测验(评论区对答案)

模式串 "AAAB" 的 next 数组(下标从0开始)为( )。

A. [-1, 0, 0, 0]

B. [-1, 0, 1, 1]

C. [-1, 0, 1, 2]

D. [-1, 0, 0, 1]

🔔 本专栏日更,点击头像 → 专栏《软考中级高频考点》订阅,第一时间接收新内容

#软考中级 #软件设计师 #KMP算法 #模式匹配 #数据结构 #软考备考

相关推荐
一米阳光86612 小时前
软考(中级)软件设计师核心笔记(8)数据结构——线性结构、数组、矩阵
数据结构·笔记·职场发展·软考·软件设计师·中级职称
晚风醉蝶3 小时前
第零篇-算法全景图
算法
疯狂打码的少年3 小时前
【数据结构】树的基本概念与二叉树定义
java·数据结构·笔记·算法
webmote333 小时前
用 NVIDIA Nemotron 3 Super + .NET 构建有记忆的多轮对话
后端·算法
马上去吃汉堡王3 小时前
Windows11安装Geant4
笔记
气泡水。3 小时前
RHCSA笔记实记
linux·笔记
Keven_113 小时前
算法札记:区分稀疏图与稠密图在经验上的数学计算差异
算法·稠密图·稀疏图
Sagittarius_A*4 小时前
后量子密码|前置基础 03|数字签名运行逻辑:签名签发、核验原理与实际业务应用
算法·信息安全·密码学·数字签名·后量子密码
DBA小马哥4 小时前
向量数据库入门到进阶:Embedding、ANN算法与RAG落地的关键术语
数据库·算法·embedding