KMP字符串匹配的原理与C代码实现

kmp的精华在于next数组,该数组存储了当子串与主串发生不匹配时应该调整的下标位置。对于next数组,直观来说就是当发生不匹配时,已经匹配的部分串里的前缀后缀的最大公共部分。以“abababca”为例,若在字符c处未匹配,已匹配部分ababab的前缀后缀最大公共部分为4,即abab;若在第二个b处发生不匹配,则最大公共部分为1,即a。若在第一个b处发生不匹配,则最大公共部分为0(最大公共部分不能是自身全长)。如何计算这个next数组的值呢?当然可以用循环的方式求取最大公共部分,如下:

int PreSuf(char a[],int b)//find maximum co-length of prefix and suffix in a string/array.
{
	int i,j,length=0;
	if(b==1)length=0;
	for(i=1;i<b;i++)
	{
		for(j=0;j<i;j++)
		{
			if(a[j]!=a[b-i+j])break;
		}
		if(j==i&&i>length)length=i;
	}
	return length;
}

但这不是最好的方式,因为这样并未有效利用子串自身的信息。实际上,next数组每个元素之间具有一定的关系。

仍以abababca为例,因为在aba中已知a是公共部分,此时若下一个字符(第四个)为b,则正好与第二个字符匹配,这样公共部分正好从a变为ab,这就是next[i+1]=next[i]+1的意义;当然,下一个字符也可能和已知串的前缀部分(公共部分)的后一个字符不匹配,比如ababab,公共部分为abab,下一个字符为c,而公共部分为abab,下一个字符为a,ac就不同。此时应该用回溯法找到公共部分abab的公共部分--ab,考察ab的下一个和c是否一致,显然,也不一致,继续回溯,直到回溯到-1,说明公共部分为0,记next[i]=0.这里请自己画图仔细理解:

1、任何一个公共部分,一方面他是作为一个子串的前后缀公共部分,另一反面,他自己本身内部又有公共部分,是一层套一层的关系。abab作为ababab的公共部分,同时内部也有公共部分--ab。

2、如1中所述,ab是abab的前缀和后缀,而abab是ababab的前缀和后缀,所以,ab同时是ababab的前缀的前缀,前缀的后缀,后缀的前缀,后缀的后缀。这里我们只需要考虑前缀的前缀,后缀的后缀,他们相等。正是这一个关系,才让回溯有了理由。

代码如下:

int Next(Str str,int next[])
{
	int i,j=0;
	next[0]=-1;
	next[1]=0;
	for(i=2;i<str.length;i++)
	{
		while(j&&str.ch[j]!=str.ch[i-1])j=next[j];
		if(str.ch[j]==str.ch[i-1])j=j+1;	
		next[i]=j;
	}
	
}

全部kmp代码如下:

int Kmp(Str str1,Str str2,int next[])
{
	int i=0,j=0;
	while(i<str1.length&&j<str2.length)
	{
		if(str1.ch[i]==str2.ch[j])
		{
			i++;
			j++;
		}
		else
		{
			j=next[j];
			if(j=-1)
			{
				j=0;
				i++;
			}
		}
	}
	if(j==str2.length)return i-str2.length;
	else return -1;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值