KMP字符串匹配算法

今天学习了一下KMP字符串匹配算法。看的是July的博客从头到尾彻底理解KMP(2014年8月22日版),写得很详细。

有两个字符串,母串S,子串P,要在S中找到P。朴素的字符串匹配算法是一一比较两个字符串中的每一个字符,同时记录连续匹配的长度,该长度与P长度一致时说明在S中找到了P。若某个字符失配,则S的索引i和P的索引j都要回退已匹配的长度,然后以下一个字符为首字符继续匹配。时间复杂度为O(m*n),m和n分别为P和S的长度。

而KMP字符串匹配利用了P串中最长共同前后缀的信息,构造出一个next数组,用于在失配时指定P的索引j回退的长度,而S的索引i不需要回退。将时间复杂度降为O(m+n)。

具体代码和注释如下。为了说明next数组构建过程中的关键递归语句k=next[k],我使用了与July不同的用例。

/*2015.8.3cyq*/
#include <iostream>
#include <string>
#include <vector>
using namespace std;

//朴素的字符串匹配,寻找主串S中的子串P,时间复杂度O(m*n)
int strMatch(const string &S,const string &P){
	int n=S.size();
	int m=P.size();
	if(n<m)
		return -1;
	int i=0,j=0;
	while(i<n&&j<m){
		if(S[i]==P[j]){//该字符匹配
			i++;
			j++;
		}else{//失配,i回退j步到匹配的首字符,然后加1
			i=i-j+1;
			j=0;
		}
	}
	if(j==m)//全部匹配,返回P在S中的位置
		return i-j;
	return -1;
}
//KMP,时间复杂度O(m+n)
//string S="AAACDAAAD ECDAACDAAEB";
//string P= "AACDAAEB";
//next数组: -10100120
//P串AACDAAE与S串AACDAAA不匹配,j从6变为2,比较AAC与S串中间的AAA
int kmpMatch(const string &S,const string &P,const vector<int> &next){
	int n=S.size();
	int m=P.size();
	if(n<m)
		return -1;
	int i=0,j=0;
	while(i<n&&j<m){
		if(j==-1||S[i]==P[j]){
			i++;
			j++;
		}else{
			j=next[j];//j=0时,若S[i]!=P[0],则j=next[0]=-1,结束递归
		}
	}
	if(j==m)
		return i-j;
	else
		return -1;
}
//获取子串P的next数组
//next[j]=k表示P[j]之前(不包括P[j])有长度为k的相同前后缀
void getNext(const string &P,vector<int> &next){
	int len=P.size();
	next[0]=-1;
	int k=-1;
	int j=0;
	while(j<len-1){
		if(k==-1||P[k]==P[j]){
			k++;
			j++;
			next[j]=k;//该行换成下面4行,可在k缩小后得到同样字符时获得优化
			//if(P[j]!=P[k])
			//	next[j]=k;
			//else
			//	next[j]=next[k];
		}else{
			k=next[k];//缩短共同前后缀的长度
			//该句很关键,以AACDAAEB为例
			//next数组为: -10100120
			//计算B所对应的next[7]时,因P[2]!=P[6],AAC与AAE不匹配,则k=next[k],k从2变为1
			//意义为E前面虽然有长度为2的AA与字符串前缀AA匹配,但E与C不匹配,
			//于是只能缩短共同前后缀长度,利用E与C前面的字符串一致的信息,
			//结合next[2]=1,即C前面有长度为1的共同前后缀,将E前面的共同前后缀长度缩短到1,
			//这样就确定P[1]与P[6]前面是已匹配的,直接比较P[1]和P[6]即可,由于AA与AE又不匹配
			//k=next[k],k从1变为0,比较P[0]与P[6],A和E又不匹配,k=next[k],k从0变为-1
			//之后k++,j++,next[7]=0,也可见递归的退出条件是匹配或者k==-1
		}
	}
}
int main(){
	string s1="AAACDAAAD ECDAACDAAEB";
	string s2= "AACDAAEB";
  //next数组为:-10100120
	int m=s2.size();
	vector<int> next(m);
	getNext(s2,next);
	for(int i=0;i<m;i++)
		cout<<next[i]<<" ";
	cout<<endl;

	cout<<"strMathch:"<<strMatch(s1,s2)<<endl;//输出13
	cout<<"KMP:"<<kmpMatch(s1,s2,next)<<endl;//输出13
	return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值