KMP算法
让我们先看看简单模式匹配算法的代码
简单模式匹配算法
//方案①
int Index(String S,String T){
int i,j;
int k=1;
while(i<=S.length&&j<=T.length){
if(S.ch[i]==T.ch[j]){
i++;
j++
}
else{
i=k;
j=1;
k++;
}
}
if(j>T.length){
return k;
}
else return 0;
}
//方案②
int Index(String S,String T){
int i,j;
while(i<=S.length&&j<=T.length){
if(S.ch[i]==T.ch[j]){
i++;
j++
}
else{
i=i-j+2;
j=1;
}
}
if(j>T.length){
return i-T.length;
}
else return 0;
}
可以看出在简单模式匹配算法中i需要回溯,这就需要较大的时间复杂度。而KMP正是对这个算法进行优化。
KMP算法以要进行匹配的模式串为重点,回溯只出现在模式串中,即只有j需要回溯变化,i则不需要回溯。而j怎么回溯呢?
这就需要用上KMP中的重点,也就是next[ ]数组。那么next[ ]数组又要怎么得到呢?这就需要对模式串进行分析。
讲到next[ ]数组不得不引入一些概念:
串的前缀:包含第一个字符且不包含最后一个字符的子串
串的后缀:包含最后一个字符且不包含第一个字符的字串
当第j个字符匹配失败时,由前1~j-1个字符组成的串记作S,则:
next[j]=S的最长相等前后缀长度+1
特别地,next[1]=0;
有了以上结论我们先看看一个例题,求出其next数组
例:求模式串’ababaa’的next数组
解答:
此外求next数组的方法也可以表现为以下形式:
可以根据求next数组的思想,写出求next数组的代码,代码如下:
void get_next(String T,int next[]){
int i=1,j=0;
next[1]=0;
while(i<T.length){
if(j==0||T.ch[i]==T.ch[j]){
++i;++j;
//若pi=pj,则next[j+1]=next[j]+1
next[i]=j;
}
else{
j=next[j];
}
}
}
接下来是KMP算法的代码:
int Index_KMP(String S,String T,int next[]){
int j=1;
int i=1;
while(i<=S.length&&j<=T.length){
if(S.ch[i]==T.ch[j]||j==0){
i++;
j++;
}
else{
j=next[j];
}
}
if(j>T.length){
return i-T.length;
}
}
综合以上得到的代码如下:
void get_next(String T,int next[]){
int i=1,j=0;
next[1]=0;
while(i<T.length){
if(j==0||T.ch[i]==T.ch[j]){
++i;++j;
//若pi=pj,则next[j+1]=next[j]+1
next[i]=j;
}
else{
j=next[j];
}
}
}
int Index_KMP(String S,String T){
int i=1,j=1;
int next[T.length+1];
get_next(T,next);
while(i<=S.length&&j<=T.length){
if(j==0||S.ch[i]==T.ch[j]){
++i;
++j;
}
else{
j=next[j];
}
}
if(j>T.length){
return i-T.length;
}
else return 0;
}
简单模式匹配算法的缺点:当某些子串与模式串能部分匹配时,主串的扫描指针i经常回溯,导致时间开销增加。最坏时间复杂度O(nm)
KMP算法:当子串和模式串不匹配时,主串指针i不回溯,模式串指针j=next[j]算法平均时间复杂度: O(n+m)