KMP算法——快速求失效函数值及其代码实现

 

  • 前缀和后缀的最大相等长度

为了更好的理解我接下来所说的回溯值的求法,这里先介绍一下,如何求一个字符串的前缀和后缀相等的最大长度,为了便于说明记为k。

注意:前缀和后缀不能为字符串本身!!!!!!

如字符串“abcab”的k为2、字符串“a”的k为0、“aaaaa”的k为4、“abcaabc”的k为3。

  • 回溯值

        在主串T中寻找模式串P的过程称为模式匹配。若匹配成功,则返回P在T中的位置,匹配失败,返回0;常用的算法有两种,一个是朴素的模式匹配算法,这里就不做过多介绍。另一个就是看书看到头晕的KMP算法~~

     这里首先来大概讲一讲KMP算法思想的关键在于那里,先通过图来看一下,KMP算法匹配的过程

上图:i=0,j=0;i=1,j=1;i=2,j=2时字符都是匹配的,当i取3,j取3的时候,字符不匹配,这时候,如果按朴素的模式匹配算法来求,这时候需要将i退回i=1处,j退回j=0处。但我们观察发现,j之前的字符串和主串中一部分已经完全匹配了,而“aba”的k(上一个标签中出现)为1,这时i不动,让j移到k处,即j=1;“aba”的前缀a已经和后缀a相等,而后缀a已经和主串中的a匹配了,所以这个时候已经不需要再匹配a。如下图:

以这样一个小小的例子我们可以发现,关键就是求得当p[j]与T[i]不匹配时,j应该退回到模式串的什么位置。模式串中每一个位置都有它对应的回溯值。这样一个和模式串下标有关的数组记为next[]。

手算快速求出next【】呢???

如“abaabcac”,默认当j=0时,next[0]=-1。

当j=1是,拿出他之前的子串,即“a”,k值为0,所以next[1]=0;

当j=2时,拿出他之前的子串,即“ab”,k为0,所以next[2]=0;

当j=3时,拿出他之前的子串,即“aba”,k为1,所以next[3]=1;

当j=4时,拿出他之前的子串,即“abaa”,k为1,所以next[4]=1;

当j=5时,拿出他之前子串,即“abaab”,k为2,所以next[5]=2;

当j=6时,拿出他之前子串,即“abaabc”,k为0,所以next[6]=0;

当j=7时,拿出他之前的子串,即“abaabca”,k为1,所以next[7]=1;

所以模式串P的失效值的数组为[-1,0,0,1,1,2,0,1];

 

  • 失效函数值的两种实现

  • 求模式失效函数值的递归算法

int getNext(int j,char p[]){
	if(j==0){
		return -1;
	}
	if(j>0){
		int k=getNext(j-1,p);
		while(k>=0){
			if(p[k]==p[j-1]){
				return k+1;
			}else{
				k=getNext(k,p);
			}
		}
		return 0;
	}
	return 0;
} 

求模式失效函数值的递推算法

void getNext(char p[],int next[]){
	int j=0;
	int k=-1;
	next[0]=-1;
	while(p[j]){
		if(k==-1||p[k]==p[j]){
			j++;
			k++;
			next[j]=k;
		}else{
			k=next[k];
		}
	}
}
  • 优化的递推算法

    void getNext(char p[],int nexval[]){
    	int j=0,k=-1;
    	nexval[j]=k;
    	while(p[j]){
    		if(k==-1||p[j]==p[k]){
    			j++;
    			k++;
    			if(p[j]==p[k]){
    				nexval[j]=nexval[k];
    			}else{
    				nexval[j]=k;
    			}
    		}else{
    			k=nexval[k];
    		}
    	}
    }

     

  • 完整的KMP算法

    #include <iostream>
    #include <cstring>
    using namespace std;
    int getNext(int j,char P[]){ 
    	if(j==0){
    		return -1;
    	}
    	while(P[j]){
    		int k=getNext(j-1,P);
    		while(k>=0){
    			if(P[j-1]==P[k]){
    				return k+1;
    			}else{
    				k=getNext(k,P);
    			}
    		}
    		return 0;
    	}
    	return 0;
    }
    void getNext(char P[],int next[]){
    	int j=0,k=-1;
    	next[0]=-1;
    	while(P[j]!='\0'){
    		if(k==-1||P[j]==P[k]){
    			j++;
    			k++;
    			next[j]=k;
    		}else{
    			k=next[k];
    		}
    	}
    	return;
    } 
    void getNextval(char P[],int nextval[]){
    	int j=0,k=-1;
    	while(P[j]!='\0'){
    		if(k==-1||P[j]==P[k]){
    			k++;
    			j++;
    			if(P[j]==P[k]){
    				nextval[j]=nextval[k];
    			}else{
    				nextval[j]=k;
    			}
    		}else{
    			k=nextval[k];
    		}
    	}
    }
    int kmpCheck(char T[],char P[],int index[]){
    	int n=strlen(P);
    	int next[n]; 
    	int k=0;
    	getNext(P,next);
    	int i=0,j=0;
    	while(j==-1||T[i]&&P[j]){
    		if(j==-1||T[i]==P[j]){
    			i++;
    			j++;
    		}else{
    			j=next[j];
    		}
    		if(j!=-1&&P[j]=='\0'){
    			index[k++]=i-j;
    			j=0;
    		}
    	} 
    	return k;
    }
    int main(){
    	char t[]="abceeeabcyyyabc";
    	char p[]="abc";
    	int index[10];
    	int n=kmpCheck(t,p,index);
    	for(int i=0;i<n;i++){
    		cout<<index[i]<<'\t';
    	}
    	cout<<endl;
    	return 0;
    } 

              本来想详细讲解一下KMP算法的原理,后来发现我自己的语言表达确实有点拙劣,倒是花了很长的时间画图,也简单说了一下如何手算一个kmp算法的回溯值矩阵。至于算法的实现就是草草的贴了几个代码。能力有限,还希望不当之处大家及时和我讲出来。

  • 21
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值