写在前面
本蒟蒻初学KMP,肯定会有很多错误,请大家指出
正文
目的
说道字符串匹配,大家首先想到的应该是朴素算法,一位一位匹配,匹配失败向右移动一位接着匹配,算法的时间复杂度是 O ( n ) O(n) O(n)
举一个简单的例子
A = “aababaabaab”
B = “aabaab”
(制作不易,但为啥就是不动???)
暴力算法时间直接上天,这时候要是能自动跳过一些错误就好了
比如 :
直接跳到
就可以避免匹配一大堆了
怎么做到呢
流程
流程
刚刚的字符串的next数组是
你先别管它咋求,你先了解他咋用!!!
当他走到这里匹配失败了
我们此时不需要将B串右移一位,而是使 B串的第 1 位(B串的第next [ j ]位对着 i ) 对着 i 进行匹配(下标从0开始)
匹配失败,使 B串的第 0 位(B串的第next [ j ]位 ) 对着 i 进行匹配
此时匹配失败,但是此时 next [ j ] == -1,i 向右移
匹配成功,i 和 j 同时向右移
接下来一直右移就完成匹配了
next数组
刚刚的next数组是什么原理呢???
i = 0 , 前面没有字符 , next [ 0 ] = -1
i = 1 , 前面有字符 “a” , 字符串 “a” 最长的相同前后缀 的长度为 0
i = 2 , 前面有字符 “aa” , 字符串 “aa” 最长的相同前后缀为 “a” 长度为 1
i = 3 , 前面有字符 “aab” , 字符串 “aab” 最长的相同前后缀 的长度为 0
i = 4 , 前面有字符 “aaba” , 字符串 “aaba” 最长的相同前后缀为 “a” 的长度为 1
i = 5 , 前面有字符 “aabaa” , 字符串 “aabaa” 最长的相同前后缀为 “aa” 的长度为 2
能有点疑问:“为啥要求 最长的相同前后缀 ???”
很好理解,当你匹配到这个时候匹配失败了
意味着前面这一段蓝色的是相同的
当你求出它前面字符串相同前后缀的长度的时候
也就是黄色的这两段相同,并且黄色这两段的长度已经求出来了
所以红色的这两段相同,并且红色这两段的长度就等于黄色那段的长度
所以直接拿 B串下标1 对准 i 继续匹配即可
再举个栗子 :
A
~~~~~
= “a a b b a b c a a b b a b a”
B
~~~~~
= “a b b a b a”
next = " -1 0 0 0 1 1 "
当他匹配到这个位置
实现
咋求 next数组 ???
我们先来看看如何求最长相同前后缀
如果此时
~
i
ABAABCABAAB
~~~~~
j
可知此处 s[i] = s[j] , next [j] = i + 1 = 1;
然后 i 往后移
~~~
i
ABAABCABAAB
~~~~~~~
j
可知此处 s[i] ≠ s[j] , i = next [i] = 0;意思是让 i 回退到上一次(由于当前求出的包含之前求出的)
~
i
ABAABCABAAB
~~~~~~~
j
可知此处 s[i] = s[j] , next [j] = i + 1 = 1;
然后 i 往后移
~~~
i
ABAABCABAAB
~~~~~~~~~~
j
可知此处 s[i] = s[j] , next [j] = i + 1 = 2;
然后 i 往后移
~~~~~
i
ABAABCABAAB
~~~~~~~~~~~~
j
可知此处 s[i] ≠ s[j] , i = next [i] = 1;
~~~
i
ABAABCABAAB
~~~~~~~~~~~~
j
可知此处 s[i] ≠ s[j] , i = next [i] = 0;
~
i
ABAABCABAAB
~~~~~~~~~~~~
j
可知此处 s[i] ≠ s[j] , i = next [i] = -1;
当 i = -1 时 , next [j] = 0 , i = 0, 接着往下就行了
~
i
ABAABCABAAB
~~~~~~~~~~~~~~
j
可知此处 s[i] = s[j] , next [j] = i + 1 = 1;
然后 i 往后移
~~~
i
ABAABCABAAB
~~~~~~~~~~~~~~~~~
j
可知此处 s[i] = s[j] , next [j] = i + 1 = 2;
然后 i 往后移
~~~~~
i
ABAABCABAAB
~~~~~~~~~~~~~~~~~~~
j
可知此处 s[i] = s[j] , next [j] = i + 1 = 3;
然后 i 往后移
~~~~~~~
i
ABAABCABAAB
~~~~~~~~~~~~~~~~~~~~~
j
可知此处 s[i] = s[j] , next [j] = i + 1 = 4;
然后 i 往后移
…………
相信大家已经有了初步的了解了,但是失配那一步为什么要 i = next [i] 呢???
这一步相当于一个回退
如图,假如相同颜色的字符串相同
突然这俩一灰一黑的不一样了,那咋办???
容易看出,next [i - 1] 是红色那一部分的长度 ,由于蓝色那一部分相同所以剩下的这两个红色是相同的
于是 i 回退到 next [i - 1] ,这两个继续比较
注:这里求的是最长相同前后缀,next数组需要整体右移一位,next[0] 补 -1(因为 next 是指当前字符前方的所有字符,并不包括当前字符)
next 数组 代码实现 :
char s[10001];
int next[10001] = {0} ,len;
void nxt () {
int j = 0 ,i = -1;
while (j < len) {
if (i == -1 || s[j] == s[i]) {
next[++ j] = ++ i;
}
else {
i = next[i];
}
}
}
匹配过程比较简单,大家可以自己写出来
后记
本蒟蒻如有错误,请大家指出
谢谢大家
——2020.8.27