KMP算法
BF算法
BF算法就是我们最基本的求解字符串匹配的算法,算法的时间复杂度为O(M*N),空间复杂度为O(1),具体过程如下:
串 | 第一次 | 第二次 | 第三次 | 第四次 |
---|---|---|---|---|
模式串S[i] | a bcababc | ab cababc | abc ababc | ab cababc |
匹配串T[j] | a babc | ab abc | aba bc | a babc |
可以看到在第三次匹配失败的时候,我们要回溯,直接S串直接i+=1,然后T串j=0从头继续开始。这样复杂度就比较高了。
KMP算法
而KMP算法就是为了解决BF算法的复杂度比较高而出现的,KMP算法的时间复杂度为O(M+N),空间复杂度为O(N),具体过程如下:
串 | 第一次 | 第二次 | 第三次 | 第四次 |
---|---|---|---|---|
模式串S[i] | a baababc | ab aababc | aba ababc | abaa babc |
匹配串T[j] | a babc | ab abc | aba bc | abab c |
第四次 | 第五次 | 第六次 | 第七次 | 第八次 |
abaa babc | abaa babc | abcab abc | abcaba bc | abcabab c |
ab abc | a babc | ab abc | aba bc | abab c |
在第四次匹配失败后,不进行回溯,而是直接对匹配串进行下一个匹配,是因为前面已经把aba匹配过了,知道前一个也是a不需要再次进行匹配,这个就是通常所说的KMP算法。而在匹配失败后到底对哪个进行再次匹配,也就需要我们求next[j]数组了。
求next数组
next数组存放的是,当匹配失败后,需要跳转到哪个字符再次进行匹配。
求的方法,手算:
例子(ababc)的next数组为:[-1 0 0 1 2].
i = 0, j=-1, next[0]=-1, next[1]=0;
next[2] = next[1+1];
next[1] = 0
T[1] != T[0];
则next[2] = next[1] = 0;
next[3] = next[2+1];
next[2] = 0;
T[2] == T[0] (T[2] = a, T[1] = b)
则next[3] = next[2]+1;
next[4] = next[3+1];
next[3] = 1;
T[3] == T[1];
则next[4] = next[3]+1 = 2;
以上是手算的解法,代码求解比较简单。
如下
代码已经改在最下面
改进后的next数组
改进的之后的next数组效率更高,但是我暂时还没找到如何手算,假如你知道,请评论给我,非常感谢。但是代码实现非常简单,就是在第一次判断之后,再进行一次判断。
代码如下:
此段代码已经删除请参考下面代码
KMP代码实现
而KMP整个的代码实现如下:
请参考博客下面代码
更新时间:2016年5月5日凌晨2点09分
新版本
今天遇到一个KMP的题目,原来发现自己的代码写的很乱,并且适用性非常低,这里放上更好的新版本代码:
首先:
计算过程是没有变化的:
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #ffffff}span.s1 {font-variant-ligatures: no-common-ligatures; color: #de38a6}span.s2 {font-variant-ligatures: no-common-ligatures}span.s3 {font-variant-ligatures: no-common-ligatures; color: #00b1ff}span.s4 {font-variant-ligatures: no-common-ligatures; color: #8b87ff}
void getNext(string W, int next[]){
memset(next, 0, sizeof(int)*(int)(W.length()+1));
for(int i=1; i<(int)W.length(); i++){
int j=i;
while(j>0){
j=next[j];
if(W[j]==W[i]){
next[i+1]=j+1;
break;
}
}
}
}
// 这个版本的代码才是我上面手写计算的思想
KMP完整实现
当然对应的KMP版本的代码需要进行一些调整:
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #ffffff}span.s1 {font-variant-ligatures: no-common-ligatures; color: #de38a6}span.s2 {font-variant-ligatures: no-common-ligatures}span.s3 {font-variant-ligatures: no-common-ligatures; color: #00b1ff}span.s4 {font-variant-ligatures: no-common-ligatures; color: #8b87ff}
int KMP_(const char *src, const char *tar, int *next){
int srcLength = (int)strlen(src);
int tarLength = (int)strlen(tar);
int i = 0, j = 0;
while (i < srcLength) {
if (src[i] == tar[j]) {
i++;
j++;
}else{
while (j != 0 && src[i] != tar[j]) {
j = next[j];
}
i++;
}
if (j == tarLength) {
return i-j;
}
}
return -1;
}
//这个是成功之后,返回匹配的位置。
当然有的要求返回匹配的次数,代码只是大同小异,只是在j == length的时候不要急着返回就好了。
代码入下:
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #ffffff}span.s1 {font-variant-ligatures: no-common-ligatures; color: #de38a6}span.s2 {font-variant-ligatures: no-common-ligatures}span.s3 {font-variant-ligatures: no-common-ligatures; color: #00b1ff}span.s4 {font-variant-ligatures: no-common-ligatures; color: #8b87ff}
int KMP(string str1, string str2, int next[]){
int len1 = (int)str1.length();
int len2 = (int)str2.length();
if(len1 > len2) return 0;
int i = 0, j = 0;
int count = 0;
while(i < len2){
if(j < len1 && str1[j] == str2[i]){
j++;
i++;
}else{
while (j > 0 && str1[j] != str2[i]) {
j = next[j];
}
}
if(j >= len1){
count++;
}
}
return count;
}//此代码是返回匹配次数