不用strings.Index而手写KMP,因其通用实现不保证最坏O(n+m),而KMP可确保稳定线性性能、支持定制回退与全匹配位置获取;computeNext需统一用0-indexed定义next[i]为pat[0:i]的最长相等真前缀后缀长度,初始化next[0]=0,j=0,遍历i时按字符相等与否更新j和next[i];匹配时区分成功(i++、j++)与失配(j>0则j=next[j-1],否则仅i++),找到匹配后j=next[j-1]以支持重叠匹配。

为什么不用 strings.Index 而要手写 KMP?
因为 strings.Index 底层确实是优化过的,但它是通用实现,不保证最坏 O(n+m);而你若在高频、确定单模式匹配场景(比如协议解析、日志关键词扫描)中需要稳定线性性能,或需获取所有匹配位置、跳过重叠匹配、甚至定制失配回退逻辑,就必须控制 next 数组构建和匹配流程。KMP 的价值不在“比暴力快多少”,而在“最坏情况不掉链子”。
computeNext 怎么写才不出错?
常见错误是把 next 定义成“最长真前缀长度”,却在匹配时当成“跳转索引”用——二者差 1。Go 中推荐直接构建 0-indexed 的 next 数组,其中 next[i] 表示模式串 pat[0:i] 的最长相等真前缀后缀长度,即下次应比较的位置索引。
- 初始化
next[0] = 0,j = 0(j 是当前最长前缀长度) - 遍历
i从 1 到len(pat)-1,若pat[i] == pat[j],则next[i] = j + 1,j++ - 否则,若
j > 0,令j = next[j-1]继续尝试;若j == 0,next[i] = 0 - 注意:不要用
append动态扩容next,预先make([]int, len(pat))更安全
匹配循环里 i 和 j 怎么更新?
核心是区分“匹配成功”和“失配”两种路径,且失配时 j 可能连续回退多次,不能只 if-else 一次。
- 主循环用
i遍历文本txt,j指向模式串当前位置 - 若
txt[i] == pat[j],i++、j++;若j == len(pat),说明找到匹配,记录i - j,然后设j = next[j-1]继续找重叠匹配(如模式"aa"在"aaa"中匹配两次) - 若失配且
j > 0,令j = next[j-1];若j == 0,仅i++ - 别忘了边界:当
i >= len(txt)或j (虽不会发生,但 <code>j始终 ≥ 0)就终止
Go 实现要注意的三个细节
一是 next 数组对空串或单字符模式必须兼容:len(pat) == 0 直接返回空结果,len(pat) == 1 时 next[0] = 0 是合法的;二是字符串用 []byte 比 string 索引更快,尤其在内层循环,建议输入统一转为 []byte;三是如果只需首次匹配,匹配成功后可直接 return,避免后续计算——KMP 的预处理无法省略,但匹配过程可以提前退出。
立即学习“go语言免费学习笔记(深入)”;
真正容易被忽略的是:KMP 的 next 数组一旦算出,可复用多次。如果你在循环中反复匹配同一模式串,务必把 computeNext 结果缓存下来,而不是每次调用都重建。


















