KMP算法

        KMP算法,适用于寻找子串的问题,其核心思路是先将子串进行自我匹配,构建出一个next数组。当寻找子串时匹配失败,就可以回到下标为当前位置的next数组里,继续开始匹配,避免了每次都得从头再来的麻烦,以此来节省时间,达到线性时间的复杂度。

        构建next数组的核心在于自匹配。首先遍历字符串,寻找与0号等同的字符,找到就记录下当前下标到next数组内,并同步检查下一位是否一样等同,是的话继续下一位,依此类推。中途若出现不等同的情况,就从0号重新开始进行匹配;但需要注意的是,这时字符串的遍历是不需要重新开始的。

         KMP的具体实现有多种不同的变形,下面的是其中一种情况,例子与百度百科的相同,但算法过程是不一样的,要注意区别。

         比如当字串为abcabcddes时,首先我们对其构建next数组,为-1000123000。过程如下:0位默认是-1,即next[0]=-1,因为不可与自己匹配。然后对1号的’b’和0号的’a’进行比较,不相等,所以next[1]=0。在这里,[1]表示1号字符,0表示回到0号字符。依此类推,直到3号字符,也就是’a’,开始同步移向下一位,所以next[3]=1(注意不匹配时,只是在进行遍历,匹配的字符仍然停留在0号)。然后发现4号字符和1号字符仍然相等,都是’b’,所以next[4]=1。同理,next[5]=2,next[6]=3。然后,7号字符’d’与4号字符’a’并不等同,所以next[7]=0,表示需要重新开始匹配(注意遍历仍在进行),回到了0号字符’a’。往后再也没有出现匹配的情况,所以next[8] = next[9] = next[10] = 0。

        构造完next数组,我们就可以开始正式进行匹配了。KMP匹配和一般的匹配差别不大,一开始也是在父串中寻找与字串0号字符相同的位置,若然找到了就开始同时检查下一个是否匹配。差别在于匹配失败时,朴素算法是重新回到最开始的字符进行重新匹配;而KMP算法则是回到匹配失败时的字串下标对应的next数组中,进行重新匹配。

        比如在dabcabcabcabcddess中寻找abcabcddes时,可以发现0号字符不匹配,但1号字符出现了匹配,并以此开始检查是否为全匹配;可是到了父串的的7号字符‘a’时,与子串中的6号字符‘c’失去了匹配的特性。在朴素算法中,这时我们需要回到父串的2号字符(因为本次是从1号字符开始检查全匹配的,但是失败了,所以需要回到本次开始的下一个字符),重新匹配子串的0号字符。但是在KMP算法中,这时我们回到的是子串的next[6]号字符,也就是3号字符’a’,而父串可以继续遍历不受影响(之前可是要回到2号字符,基本等同于重新开始啊)。仔细观察就可以发现,父串可以继续遍历是因为7号字符‘a’之前的“abc”仍然和字串的前3个字符相匹配,就省去了重复匹配的麻烦,而这正是next数组的功劳。

        所以,当字串自匹配程度越高时,KMP算法的优势也就越明显。

       假设现在要在字符串a中寻找字符串b,并返回b第一次出现在a中的下标,如果不存在的话就返回-1。由于字符串是从0开始计算的,把上面的下标信息全部-1即可。

计算next数组的函数如下:

void f( string b, int next[] ) {
	next[ 0 ] = -1;
	int j = 0;
	for ( int i = 1; i < b.size(); i++ ) {
		next[ i ] = j;
		if ( b[ i ] == b[ j ] )
			j++;
		else if ( j != 0 )
			j = next[ j ];
	}
}
而匹配函数为:

int match( string a, string b, int next[] ) {
	int j = 0;
	for ( int i = 0; i < a.size(); i++ ) {
		if ( a[ i ] == b[ j ] )
			j++;
		else if ( j != 0 ) {
			j = next[ j ];
			i--;
		}
		if ( j == b.size() )
			return i - b.size() + 1;
	}
	return -1;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值