CSP-J/S · 字符串

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] 最长相等真前后缀长度;求它就是"模式串自己匹配自己"。
② 不回退
失配时 j = next[j],文本指针 i 永不后退 → 总共 O(n+m)。
③ 一句口诀
while (j && 不等) j = nxt[j]; —— 求 next 和匹配都用它。
④ 延伸
最短循环节 = n − next[n];匹配也可用字符串哈希。