KMP 字符串匹配
O(n+m) 找子串。失配时文本指针不回退,模式串靠 next 数组"聪明右滑",保留已匹配的前缀。
一、单步演示:失配也不从头来
在文本 ABABABC 里找 ABABC。绿=已匹配,黄=正在比,红=失配。注意第 5 步失配时,模式串不退回开头,而是右滑、保留前缀 AB 继续。
文本 s
模式 p
点"下一步"开始。
p = ABABC 的 next 数组:
二、模板(C++14)
// 求 next(前缀函数):自己匹配自己
nxt[1] = 0;
for (int i = 2, j = 0; i <= m; i++) {
while (j && p[i] != p[j+1]) j = nxt[j];
if (p[i] == p[j+1]) j++;
nxt[i] = j;
}
// 匹配:文本指针 i 从头到尾只走一遍,绝不回退
for (int i = 1, j = 0; i <= n; i++) {
while (j && s[i] != p[j+1]) j = nxt[j]; // 失配就按 next 右滑
if (s[i] == p[j+1]) j++;
if (j == m) { cout << i - m + 1 << "\n"; j = nxt[j]; }
}
两段代码同构,核心都是 while (j && 不等) j = nxt[j];。字符串匹配也能用 哈希,KMP 的好处是稳定 O(n+m) 且能算循环节。
小结
① next 数组
next[i] = p[1..i] 最长相等真前后缀长度;求它就是"模式串自己匹配自己"。
next[i] = p[1..i] 最长相等真前后缀长度;求它就是"模式串自己匹配自己"。
② 不回退
失配时 j = next[j],文本指针 i 永不后退 → 总共 O(n+m)。
失配时 j = next[j],文本指针 i 永不后退 → 总共 O(n+m)。
③ 一句口诀
while (j && 不等) j = nxt[j]; —— 求 next 和匹配都用它。④ 延伸
最短循环节 = n − next[n];匹配也可用字符串哈希。
最短循环节 = n − next[n];匹配也可用字符串哈希。