KMP 算法

1358 字
7 分钟
KMP 算法
Warning

含AI生成内容

KMP 算法#

核心思想#

KMP(Knuth-Morris-Pratt)算法是 的模式匹配优化算法。其核心思想是:利用已匹配信息确定模式串的滑动位置,主串指针永不回退

BF算法(朴素模式匹配) 每次失配都要将主串指针回退到起点的下一位,大量比较被浪费。KMP 的洞察是:失配时,模式串中已匹配的部分包含了足够的信息来决定下一步从哪里继续比较。通过预先计算的 next 数组,模式串可以跳过那些不可能匹配的位置,而主串指针始终向前移动。

BF 算法(失配时): KMP 算法(失配时):
主串 i 回溯 主串 i 不动
模式串 j 归零 模式串 j = next[j],向右滑动

KMP 算法的时间复杂度为 O(n+m),远优于 BF 的 O(nm)。

next 数组(考研必考)#

next 数组的含义#

next[j] 表示:当模式串第 j 个字符与主串失配时,模式串应回退到第 next[j] 个字符继续比较。

手工求解步骤#

对模式串 T[1..j-1](即第 j 个字符之前的子串),找其最长相等前后缀的长度 k,则 next[j] = k + 1

特别规定next[1] = 0(第 1 个字符就失配,表示主串 i 要后移,模式串从头开始)。

手工求解口诀:看第 j 个字符前面的子串,找最长相等前后缀长度,加 1 就是 next[j]。

求解示例#

以模式串 T = "abaabcac"(下标从 1 开始)为例:

j子串 T[1..j-1]最长相等前后缀长度 knext[j] = k+1
1(空)0(规定)
2”a”01
3”ab”01
4”aba""a”12
5”abaa""a”12
6”abaab""ab”23
7”abaabc”01
8”abaabca""a”12

最终结果:next[] = {0, 1, 1, 2, 2, 3, 1, 2}

两种定义的辨析#

定义方式next[1]适用教材影响
下标从 1 开始,next[1] = 00严蔚敏版《数据结构》(408 教材)考研默认使用
下标从 0 开始,next[0] = -1-1部分程序设计教材代码实现不同

做题时务必先看清楚题目用的是哪种定义,否则整个数组会错位。

代码实现#

// 求 next 数组(串下标从 1 开始)
void getNext(char T[], int next[], int m) {
int j = 1, k = 0;
next[1] = 0;
while (j < m) {
if (k == 0 || T[j] == T[k]) {
j++;
k++;
next[j] = k;
} else {
k = next[k]; // k 回退(核心步骤)
}
}
}

代码要点:

  • k == 0 表示已退无可退,next[j+1] = 1
  • T[j] == T[k] 表示前后缀可以扩展一位,next[j+1] = k+1
  • k = next[k] 是利用已有 next 值进行回退,这一步骤的思想和 KMP 匹配过程完全一致

KMP 匹配过程#

int KMP(char S[], char T[], int next[], int n, int m) {
int i = 1, j = 1;
while (i <= n && j <= m) {
if (j == 0 || S[i] == T[j]) {
i++;
j++;
} else {
j = next[j]; // 主串 i 不回溯,模式串 j 回退
}
}
if (j > m) return i - m;
return -1;
}

匹配过程关键点:

  1. j == 0 时说明模式串第 1 个字符就失配,此时 i++, j++(主串后移一位,模式串从头开始)
  2. S[i] == T[j] 时两个指针同时后移
  3. 失配时 j = next[j],模式串右滑,跳过不可能匹配的位置

nextval 优化#

为什么需要 nextval#

next 数组存在一个问题:如果 T[j] == T[next[j]],那么回退到 next[j] 后必然还是失配(因为和同一个字符比较),造成多余比较。

手工求 nextval(考研常考)#

先求出 next 数组,再逐个修正:对每个 j,若 T[j] == T[next[j]],则 nextval[j] = nextval[next[j]];否则 nextval[j] = next[j]

T = "abaabcac" 为例:

jT[j]next[j]T[next[j]]是否相等nextval[j]
1a00
2b1ab != a1
3a1aa == a0(取 nextval[1])
4a2ba != b2
5b2bb == b1(取 nextval[2])
6c3ac != a3
7a1aa == a0(取 nextval[1])
8c2bc != b2

最终结果:nextval[] = {0, 1, 0, 2, 1, 3, 0, 2}

代码实现#

void getNextval(char T[], int nextval[], int m) {
int j = 1, k = 0;
nextval[1] = 0;
while (j < m) {
if (k == 0 || T[j] == T[k]) {
j++;
k++;
if (T[j] != T[k])
nextval[j] = k;
else
nextval[j] = nextval[k]; // 相等则继续回退
} else {
k = nextval[k];
}
}
}

复杂度分析#

阶段时间复杂度说明
求 next 数组O(m)m 为模式串长度,指针不回溯
KMP 匹配O(n)n 为主串长度,主串指针不回溯
总体O(n+m)远优于 BF 的 O(n x m)

空间复杂度:O(m),需要长度为 m 的 next(或 nextval)数组。

BF 算法 vs KMP 算法对比#

对比项BF 算法KMP 算法
核心策略穷举所有对齐位置利用已匹配信息滑动
主串指针失配时回退永不回退
模式串指针失配时归零失配时 j = next[j]
预处理需求 next 数组 O(m)
最好时间复杂度O(n+m)O(n+m)
最坏时间复杂度O(nm)O(n+m)
空间复杂度O(1)O(m)
适用场景短模式串、简单匹配长模式串、重复字符多

考研高频考点#

  • 手工求 next 数组(选择题/填空题每年必考,务必熟练)
  • 手工求 nextval 数组(在 next 基础上修正,常与 next 一起考)
  • KMP 算法的时间复杂度 O(n+m) 及其与 BF 算法 O(nm) 的对比
  • next 数组的含义:最长相等前后缀长度 + 1(概念题)
  • KMP 主串指针不回溯的特性(简答题/判断题)
  • 给定 next 数组模拟 KMP 匹配过程(手动模拟题)
  • next 数组求解代码中 k = next[k] 的含义(代码分析题)
  • next 数组两种定义的辨析(下标从 0 开始 vs 从 1 开始)

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

KMP 算法
https://lingluoa.icu/posts/kmp-algorithm/
作者
lingluoa
发布于
2026-07-07
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
lingluoa
Hello, I'm lingluoa.
公告
欢迎来到我的博客!不定期更新中。
文章目录
标签
站点统计
文章
53
分类
9
标签
75
总字数
175,771
运行时长
0
最后活动
0 天前
站点信息
构建平台
ESA Pages
博客版本
Firefly v6.15.6
文章许可
CC BY-NC-SA 4.0