在保守代码虐待之后,舒缓心情的最好方式就是记录一篇博客
(不知为何难以缩进) ——阿寇
A brief/simple introduction of KMP
KMP算法是一个实现字符串匹配的高级算法。
- 普通算法的实现通常比较简单,比如串
ababad
匹配abad
。假设我们把第一个串称作string,第二个串称作pattern。常规做法:a-a;b-b;a-a;b不能匹配d,此时从string的第二个字符开始重新和pattern匹配,发现b不能和a匹配;再从string的第三个字符依次和pattern匹配,最终成功。 - 很显然是一个 双循环,复杂度为
O(strlen(string)*strlen(pattern))
; - 而KMP算法则采用了非常大的脑洞,通过寻找pattern子串最长前后缀来进行高效的字符串匹配操作。
前后缀是什么?
- For example,拿abad这个pattern来说,a是ab的前缀,b是ab的后缀;a和ab是aba的前缀,a和ba是aba的后缀;a、ab、aba是abad的前缀,d、ad、bad是abad的后缀。
计算next/match/failure数组
- 同样,以abad为例。next数组是为pattern求的,所以pattern的长度等于next数组的长度。
- next[0]默认为-1;由于ab的前缀和后缀没有匹配的,所以next[1]=-1;由于aba的前缀之一a和后缀之一a匹配,因此next[2]=前缀a出现的下标=0;而abcd的前后缀无一匹配,因此next[3]=-1;
我尽量举例解释,根据例子理解吧~如果不懂,欢迎追问哦
代码如下:(参考浙江大学数据结构慕课代码)
void BuildMatch(char *pattern,int *match){
int m = strlen(pattern); //pattern 长度
match[0] = -1;//首位默认值
Position i,j;//i为循环变量 ,j是中间变量
for(i =1;i<m;i++){//pattern每一个字符都对应next值,因此进行for循环
j = match[i-1] + 1;//灵魂赋值,在计算该位字符next值的时候,
//如果借鉴前一位字符的next值,会事半功倍,
//为什么要+1呢,假设计算abababc的next数组,
//并且前三位aba已经得到,开始计算b的next值,
//显然第三位a的next值为0,而此时比较第0+1位和当前位,都是b,则
//可以得到当前位的next值为0+1;
while( j>0 && (pattern[i]!=pattern[j]))
//如果j对应的pattern字符存在前缀并且当前位无法和当前的前一位的
//前缀的下一位匹配的话,继续回溯
j = match[j-1]+1;
//退出循环要么匹配成功,要么实在找不到前缀
if(pattern[i]==pattern[j]) match[i] = j;
//匹配成功就赋值
else match[i] = -1;
//实在找不到就赋值-1
printf("match[%d]=%d\n",i,match[i]);
}
}
网上还有很多教程,要是实在看不懂,就再看看别人的博客吧
KMP
Position KMP(char *string,char *pattern){
int m = strlen(pattern);
int n = strlen(string);
if(n<m) return NOT_FIND;
Position i = 0,j = 0,*match;
match = (Position *)malloc(sizeof(Position)*m);
BuildMatch(pattern,match);
while(j<m && i<n){
if(string[i] == pattern[j]){
++i; ++j;
}
else if(j>0) j = match[j-1]+1; //回溯
else ++i;
}
return (j==m )?(i-m):NOT_FIND;
//按照数组下标来考虑的话,到m-1就可以认为匹配成功,但是
//我觉得++i存在延迟,只要相等就会++;因此最后还是多加了个1;
//因此用j==m进行判断。
}
Whole code
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define NOT_FIND -1
typedef int Position;
void BuildMatch(char *pattern,int *match){
int m = strlen(pattern);
match[0] = -1;
Position i,j;
for(i =1;i<m;i++){
j = match[i-1] + 1;
while( j>0 && (pattern[i]!=pattern[j]))
j = match[j-1]+1;
if(pattern[i]==pattern[j]) match[i] = j;
else match[i] = -1;
printf("match[%d]=%d\n",i,match[i]);
/*if(i>1) {
j = match[i-1]+1;
if(pattern[i]==pattern[j]) match[i] = j;
}
else */
}
}
Position KMP(char *string,char *pattern){
int m = strlen(pattern);
int n = strlen(string);
if(n<m) return NOT_FIND;
Position i = 0,j = 0,*match;
match = (Position *)malloc(sizeof(Position)*m);
BuildMatch(pattern,match);
while(j<m && i<n){
if(string[i] == pattern[j]){
++i; ++j;
}
else if(j>0) j = match[j-1]+1;
else ++i;
}
return (j==m )?(i-m):NOT_FIND;
}
int main(){
char string[] = "this is a not good choice";
char pattern[] = "good"; //Obviously the answer is 14;
Position answer = KMP(string,pattern);
// printf("Position of pattern in string is:",answer);
printf("%d",answer);
// printf("%d",strlen(pattern));
return 0;
}