字符串匹配
暴力匹配算法
暴力匹配算法的时间复杂度是O(n*m),其中n是目标字符串的长度,m是要查找的字符串的长度
1 | // 字符串定义 |
1 | /** |
KMP算法
KMP算法的时间复杂度是O(n+m),其中n是目标字符串的长度,m是要查找的字符串的长度,KMP算法的核心是next数组
1 | int match_find(String s, String t, const int next[]) { |
next数组
next数组手算
next数组的计算过程可以看作模式串与自身的匹配过程
例1
1 | a b a b a |
在如上字符串的next数组中:
第一位元素为-1。第一位元素之前没有字符串,无法取前后缀
1 | ↓ |
第二位元素为0。第二位元素之前的字符串,前后缀皆为空集,没有相同前后缀
1 | ↓ |
第三位元素为0。第三位元素之前字符串的前缀为{a},后缀为{b},没有相同前后缀,当第三位元素匹配失败时,从字符串第一位开始匹配(下标0)
1 | ↓ |
第四位元素为1。第四位元素之前字符串的前缀为{a, ab},后缀为{a, ba},最长相同前后缀为a,当第四位元素匹配失败时,从字符串第二位开始匹配(下标1)
1 | ↓ |
第五位元素为2。第五位元素之前字符串的前缀为{a, ab, aba},后缀为{b, ab, bab},最长相同前后缀为ab,当第五位元素匹配失败时,从字符串第三位开始匹配(下标2)
1 | ↓ |
其实就是在某个字符之前的所有字符串里,找开头和结尾相同的最长字符串的长度,这个长度可以作为当前字符的next值,当匹配失败时,就可以根据next值来调整指针位置,从而减少匹配次数
例2
1 | a b a a b c |
在如上字符串的next数组中:
第一位元素为-1。第一位元素之前没有字符串,无法取前后缀
第二位元素为0。第二位元素之前的字符串,前后缀皆为空集,没有相同前后缀
第三位元素为0。第三位元素之前字符串的前缀为{a},后缀为{b},没有相同前后缀,从字符串第一位开始匹配(下标0)
第四位元素为1。第四位元素之前字符串的前缀为{a, ab},后缀为{a, ba},最长相同前后缀为a,从字符串第二位开始匹配(下标1)
第五位元素为1。第五位元素之前字符串的前缀为{a, ab, aba},后缀为{a, aa, baa},最长相同前后缀为a,从字符串第二位开始匹配(下标1)
第六位元素为2。第六位元素之前字符串的前缀为{a, ab, aba, abaa},后缀为{b, ab, aab, baab},最长相同前后缀为ab,从字符串第三位开始匹配(下标2)
1 | a b a a b c |
例3
1 | a b c a b a b |
在如上字符串的next数组中:
第一位元素为-1。第一位元素之前没有字符串,无法取前后缀
第二位元素为0。第二位元素之前的字符串,前后缀皆为空集,没有相同前后缀
第三位元素为0。第三位元素之前字符串的前缀为{a},后缀为{b},没有相同前后缀,从字符串第一位开始匹配(下标0)
第四位元素为0。第四位元素之前字符串的前缀为{a, ab},后缀为{c, bc},没有相同前后缀,从字符串第一位开始匹配(下标0)
第五位元素为1。第五位元素之前字符串的前缀为{a, ab, abc},后缀为{a, ca, bca},最长相同前后缀为a,从字符串第二位开始匹配(下标1)
第六位元素为2。第六位元素之前字符串的前缀为{a, ab, abc, abca},后缀为{b, ab, cab, bcab},最长相同前后缀为ab,从字符串第三位开始匹配(下标2)
第七位元素为1。第七位元素之前字符串的前缀为{a, ab, abc, abca, abcab},后缀为{a, ba, aba, caba, bcaba},最长相同前后缀为a,从字符串第二位开始匹配(下标1)
1 | a b c a b a b |
next数组计算
next数组的计算过程可以看作模式串与自身的匹配过程,计算next数组的时间复杂度是O(m),其中m是模式串的长度
1 | void get_next(String str, int next[]) { |
nextval数组
nextval是对next数组的优化
1 | void get_next(String str, int next[]) { |