朴素查找与kmp算法

本文介绍了朴素查找和KMP算法,重点解析了KMP算法中的next数组计算方法。通过实例阐述了next数组的意义,说明了如何利用next数组提高匹配效率,降低时间复杂度至O(n)。
摘要由CSDN通过智能技术生成

朴素查找

int search1(const char *str,const char *childstr)
{
	int i;
	int len = strlen(childstr);
	for(i = 0;i!='\0';i++)
	{
		if(strncmp(str+i,childstr,len)==0)
		{
			return i;
		}
	}
	return -1;
} 
int search(const char *str,const char *childstr){
	int i,j;
	int len = strlen(childstr);
	for(i=0,j=0;str[i]!='\0'&&childstr[j]!='\0';){//n 
		if(str[i] == childstr[j]){//如果相等继续比较下一个 
			i++;
			j++;
		}else{//如果不相等,str回溯到 j 个字符串之前+1 
			i = i-j+1;//不相等的时候 主串回溯 
			j = 0;	
		}
	}
	if(childstr[j]=='\0') 
		return i-j;
	return -1;
}

kmp算法

kmp算法最主要的是理解next数组的计算方法

首先声明在不同的地方对next数组的定义和计算有所不同,为了避免混淆,在此仅介绍一种。

next数组是对于模式字符串也就是需要被匹配字符串其中的每一个字母而言,为了在匹配时避免重复判断而存在,下面通过一个实例来介绍next数组的意义和具体用法。

首先摆出next数组

a 匹配串:b a b a b b;

0 0 1 2 3 1;

列如 s原串:b a b a b a b c b a b a b a b a b b;

a匹配串:b a b a b b;

先按暴力匹配可以得到如下

原串:b a b a b a bcb a b a b a b a b b;

匹配串 b a bab b;

在如图黑色字母处出现不匹配,如果继续暴力,则会将匹配串向后移一位,然后再重新将匹配串的指针指向首位重新开始逐个匹配。但现在思考一下,我已经匹配了过了前三个字符了,可不可以合理地利用已知的条件呢?

next数组就起到了运用已知条件的作用。如next[2]=1(从0开始),代表从匹配串队首开始向后走1(包括此点)与从a[2]开始向前走1的字符串完全相同。如next[3]=2,代表a[0],a[1]所组成的字符串与a[2]a[3]组成的完全相同。next[n]就代表n满足上述条件的长度。

这样的话next数组的求解就十分容易了,只需o(n)的复杂度;next[0]必为0,对于求解next[n],采用递推思想,如果next[n]=k;(k>0)则必须next[n-1]=k-1;如果next[n-1]=0;则看next[n]是否等于next[0]。如等于则next[n]=1。

接下来就是实现时next数组的现实作用了,如上图,设原串的指针为i,匹配串的指针为j。当匹配到a[3]失败时,要注意因为从a[j]即a[3]开始向前走next[j-1],即1与从队首开始向后走next[j-1]的字符是相同的,所以j回到next[j-1],即a[1]的位置,而i指针不变,再进行比较。这样就能高效地进行匹配。

结合图像来看,如右下图加黑的B与A部分就是匹配失败时next[j-1]的值代表B部分与A部分完全相同,所以直接跳过这部分,直接将j指针移到A部分的后一位处,即next[j-1]处。

//kmp  O(n+m)  主串不回溯   调整j的位置 
//当childstr[j] 和 主串不相等时  j退回到哪一个字符进行比较
void getNext(const char *pattern,int next[]){
	next[0] = -1;
	int len = strlen(pattern);
	int j=0,k=-1;//next[j]   pattern[0,j-1] 最大重复子串 
	while(j<len-1){
		if(k==-1 || pattern[j] == pattern[k]){
			//next[++j] = ++k;
			//优化next数组 
			if(pattern[++j] == pattern[++k]){
				next[j] = next[k];
			}else{
				next[j] = k;
			}
		}else{//前缀的最后一个字符和后缀最后一个字符不相等 
			k = next[k];
		}
	} 
} 
//str主串  pattern模式串 
int kmp(const char *str,const char *pattern){
	int i=0,j=0;
	int next[strlen(pattern)];
	getNext(pattern,next);
	while(str[i]!='\0'&&pattern[j]!='\0'){
		//j==-1 第一个字符就不相等 只能改变j不能改变i 
		if(j==-1||str[i]==pattern[j]){
			++i;
			++j;
		}else{
			j = next[j];//j回退到指定的位置继续比较 
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HOVL_C++

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值