KMP Boundary Next
KMP 的分界线法与 next 数组
KMP 解决的问题与朴素模式匹配相同:在主串中查找模式串第一次出现的位置。区别在于:KMP 发生失配时,主串指针 i 不回退,只根据模式串自身结构调整模式串指针 j。
核心思想
当 S[i] != T[j] 时,T[1..j-1] 已经与主串中的一段字符完全相同。KMP 利用这段“已知匹配信息”,判断模式串应该滑到哪里继续比较。
更直观的手算方式:
- 在失配位置前画一条分界线。
- 只看分界线之前已经匹配的部分。
- 让模式串一步一步后退,直到分界线之前模式串部分能与相应位置的主串部分还能对上,或分界线前的模式串部分为$\emptyset$
- 此时
j指向的位置,就是next[j]。
这种做法和“最大相等前后缀”本质等价,但计算时更贴近匹配过程:每个 next[j] 都对应“T[j] 失配后该拿谁继续和当前 S[i] 比”。
next 的含义
采用教材常见的 1-based 串下标:
1 | if (S.ch[i] == T.ch[j]) { |
next[j] 表示:当 T[j] 与 S[i] 失配,且 T[1..j-1] 已经匹配时,模式串指针 j 应退到的位置。
常见约定:
next[1] = 0:第一个字符失配,说明当前主串字符无法匹配模式串开头;下一步应移动主串指针。next[2] = 1:第二个字符失配时,尝试让模式串第一个字符与当前主串字符比较。- 其他
next[j]用分界线法计算。
KMP 匹配代码
1 | int KMP_Index(SString s, SString t, int next[]) { |
j == 0 的含义:模式串第一个字符也无法匹配当前主串字符,需要让主串指针前进,并让 j 回到 1。
手算 next 的要点
Example
| j | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| T[j] | a | b | a | a | b | c |
| next[j] | 0 | 1 | 1 | 2 | 2 | 3 |
理解方式:
next[1] = 0固定。next[2] = 1固定。next[6] = 3:若c失配,分界线前是abaab,后退后可保留前后能对上的ab,下一次用T[3]继续比较。
复杂度
KMP 总复杂度为 $O(n + m)$:
- 求
next数组需要 $O(m)$。 - 匹配过程主串指针不回退,最多线性扫描主串,复杂度 $O(n)$。
优化
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Elian's blog page!