字符串前缀 与字符串后缀
字符串前缀(
Proper prefix
) :包含第一个字符,不包含最后一个字符的所有子串
例如:abababca
的前缀:a、ab、aba、abab、ababa、ababab、abababc
字符串后缀(
Proper suffix
):不包含第一个字符,包含最后一个字符的所有子串
例如:abababca
的后缀:a、ca、bca、abca、babca、ababca、bababca
字符串部分匹配表
字符串部分匹配表 (Partial Match Table
) 也称为 next
数组,例如:abababca
的部分匹配表为:
char | a | b | a | b | a | b | c | a |
---|---|---|---|---|---|---|---|---|
index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
value | 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 |
每列 value
值表示前 index + 1
个字符子串的最大字符串前缀与字符串后缀相匹配的长度,例如:
- index 为 0 的子串为 a,没有字符串前缀和字符串后缀,所以 value 为 0
- index 为 1 的子串为 ab,字符串前缀为 a,字符串后缀为 b,没有相匹配的字符串前缀与字符串后缀,所以 value 为 0
- index 为 2 的子串为 aba,字符串前缀为 a、ab,字符串后缀为 a、ba,字符串前缀与字符串后缀相匹配的子串为 a,长度为 1,所以 value 为 1
- index 为 3 的子串为 abab,字符串前缀为 a、ab、aba,字符串后缀为 b、ab、bab,字符串前缀与字符串后缀相匹配的子串为 ab,长度 2,所以 value 为 2
- index 为 4 的子串为 ababa,字符串前缀为 a、ab、aba、abab,字符串后缀为 a、ba、aba、baba,字符串前缀与字符串后缀相匹配的子串为 a、aba,长度最大的为 aba ,所以 value 为 3
- …
- index 为 7 的子串为 abababca,字符串前缀为 a、ab、aba、abab、ababa、ababab、abababc,字符串后缀为 a、ca、bca、abca、babca、ababca、bababca,字符串前缀与字符串后缀相匹配的子串为 a,所以 value 为 1
KMP 算法思路
KMP 算法就是利用字符串部分匹配表可以计算出当模式串与主串不匹配时,模式串可以多后移几位 (默认后移 1 位)
当模式串与主串不匹配时,如果不匹配字符对应模式串下标大于 0
(非首个模式串字符),取此字符前一个字符对应字符串部分匹配表中的 value
,如果 value
大于 0
,模式串中不匹配字符之前 (不含不匹配字符) 子串长度减去 value
即模式串为后移的位数。
暴力匹配算法当模式串和主串不匹配时,主串匹配下标 +1
,模式串匹配下标置为 0
,KMP
算法优化点在于模式串匹配下标置为 value
。
例如在主串 bacbababaabcbab
中查找模式串 abababca
- 第一次符合以上规则的情况如下,模式串与主串不匹配字符 (
b
) 前一个字符为a
,对应字符串部分匹配表index
为0
,value
为0
,所以不存在多后移情况
1 | bacbababaabcbab |
- 第二次符合以上规则的情况如下
1 | bacbababaabcbab |
- 模式串与主串不匹配字符 (
b
) 前一个字符是a
,对应字符串部分匹配表index
为4
,value
为3
,不匹配字符之前模式串为ababa
长度为5
, 所以后移5-3=2
位
1 | bacbababaabcbab |
- 模式串与主串不匹配字符 (
b
) 前一个字符是a
,对应字符串部分匹配表index
为2
,value
为1
,不匹配字符之前模式串为aba
长度为3
, 所以后移3-1=2
位
1 | bacbababaabcbab |
- 此时,模式串长度已经比剩余主串长,匹配结束。
代码实现
next 数组的代码实现思路参考 KMP 算法的 Next 数组详解
Golang 暴力匹配代码实现
1 |
|
Golang KMP 代码实现
1 | func Kmp(s, p string) int { |
Java KMP 代码实现
1 | package io.github.ehlxr.algorithm.match; |