直接手写KMP易失败因忽略next数组本质是失配跳转位置而非单纯前后缀长度,且常错在j回退边界处理(j==0时不可再退)及next[0]取0还是-1的混用,推荐统一用next[0]=0版本。

为什么直接手写 kmp_search 容易匹配失败?
因为多数人只关注主串遍历,却漏掉 next 数组的构建逻辑——它不是“最长公共前后缀长度”,而是“当失配时,模式串该跳到哪个位置继续比”。这个位置必须保证已匹配部分的后缀和模式串前缀重叠,否则会漏解。常见错误是用 i 从 0 开始递推 next[i],但没处理好 j = next[j-1] 的回退边界(j == 0 时不能再退)。
正确做法是双指针:i 扫描模式串下标,j 记录当前最长匹配前缀长度(即 next[i-1]),每次比较 pattern[i] 和 pattern[j]:
- 相等 →
next[i] = ++j - 不等且
j > 0→j = next[j-1]继续尝试缩回 - 不等且
j == 0→next[i] = 0
next 数组要填 0 还是 -1?
两种主流实现:一种让 next[0] = 0,搜索时用 if (j > 0) j = next[j-1];另一种设 next[0] = -1,搜索循环里直接 j = next[j]。前者更直观,后者在某些教材里更紧凑。但混用会崩溃——比如用 next[0] = -1 的数组,却在搜索中写 j = next[j-1],第一次 j=0 就访问 next[-1]。
推荐统一用 next[0] = 0 版本,避免负索引风险,尤其在 C++ 中未开启边界检查时极易段错误。示例片段:
立即学习“C++免费学习笔记(深入)”;
vector<int> build_next(const string& p) {
vector<int> next(p.size(), 0);
int j = 0;
for (int i = 1; i < p.size(); ++i) {
while (j > 0 && p[i] != p[j]) j = next[j-1];
if (p[i] == p[j]) ++j;
next[i] = j;
}
return next;
}匹配过程里 j 什么时候归零?
j 是模式串当前已匹配长度,不是下标。它只在两种情况下归零:一是主串某位置完全无法匹配(j==0 && text[i] != pattern[0]),此时 i++ 继续;二是成功匹配后手动重置(如找所有匹配点时)。别在每次失配后无脑 j = 0,那就退化成暴力算法了。
关键判断逻辑是:
- 若
text[i] == pattern[j]→i++, j++ - 若
text[i] != pattern[j]且j > 0→j = next[j-1] - 若
text[i] != pattern[j]且j == 0→i++(跳过当前主串字符)
当 j == pattern.size() 时,说明找到一个匹配,起始位置是 i - j。
边界情况:空串、单字符、全相同字符怎么处理?
空模式串应返回 0 或抛异常,C++ 标准库倾向返回 string::npos;单字符时 next 数组只有 next[0]=0,匹配逻辑照常;全相同字符(如 "aaaa")会导致 next = [0,1,2,3],此时失配回退链很长,但只要循环条件写对(用 while 而非 if),就能正确跳转。最容易错的是模式串长度为 1 时忘记在匹配后立即检查 j == 1,导致漏掉末尾匹配。
实操建议:写完立刻测三组用例:text="a", pattern="a";text="aa", pattern="aa";text="ababababca", pattern="abababca"(含经典陷阱:前缀 ababab 和后缀 ababca 的重叠部分只有 "ab")。


















