KMP算法——在字符串中找字符串

  • 字符串中寻找字符串,可能最容易先想到的是---暴力匹配算法。比如:

这张图中的主串与子串进行比对,一个字一个字的比对,当主串第四个B与子串第四个A不匹配时,主串就会跳过第一个字符,到第二个字符重新匹配,直到与子串的四个字符都匹配上,才算完成。这个算法原理很简单,理解起来也不难,但最大的问题就是它的时间复杂度。时间复杂度是一个o(n*m)的算法,n和m代表:主串和子串的长度。如果用暴力算法的话,效率极低,但我们可以用KMP算法,这样就很简单了。

  • 那什么时KMP算法呢?通俗的来讲:KMP算法是一种改进的字符串匹配算法,(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少子串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)。
  • 我们就在想,子串中最后一个数不等于主串中的第四个数,我们可不可以把子串往后移呢。这样就总能找到与主串相等的数了。那,怎样才能找到跳过多少个字符呢?如何找到呢?这里就需要用到KMP中的定义数组next了。以一个图为例:对于next的数组,子串中最后一个数匹配失败后,它所对应的数为2,则子串就跳过2个字符,然后再看到主串与子串有无匹配。这样就不需要回退主串中的指针了。只需要子串跳过n个字符,与之匹配就可以了。是不是比之前的暴力算法简单了很多。
  • 首先,要解决的问题就是Next数组的的生成。next的本质:就是寻找子串中“相同前后缀的长度”,并且一定时最长的前后缀。子串中第一个A,显然不存在比他还要短的前后缀,(第二个也一样)所以为0。第三个也为A,则为1,第四个也为A则为2。如果子串还有很多串,则以此类推。我们可以用for循环暴力来求,但效率比较低,我们可以用递推的方式来求解。

则代码为:

def build_next(patt)
{
	//这里要计算next数组
	next = [0];//初始化,初识元素为1
	prefix_len = 0;
	i = 1;
	while (i < len(patt))
		if (patt[prefix_len] == patt[i])
			prefix_len += 1;
	next.append(prefix_len)
		i += 1;
		else
			if (prefix_len == 0)
				next.append(0);
	i += 1;
			else
				prefix_len = next[prefix - 1]
				return next;
}

这样next就能很好的表示出来啦。

  • 那next数组已经实现了,我们就可以来实现KMP算法的程序实现了。则代码为
    def kmp_search(string, patt)
    {
    	next = build_next(patt);//next算法已经算出
    	i = 0;//主串中的指针
    	j = 0;//子串中的指针
    	while (i < len(string))
    		if (string[i] == patt[j])//字符匹配,指针后移
    			i += 1;
    	j += 1;
    		else if (j > 0)//字符失配,根据next跳过子串前面的一些字符
    			j = next[j - 1];
    	//子串第一个字符就失配
    		else
    			i += 1;
    	if (j == len(patt))//匹配成功
    		return i - j;
    		
    }
    这里就是KMP算法的的程序实现,但是还没有完,还需要主函数的帮衬,才能完成这项算法,这一步就比较简单了,则代码为:
    int main()
    
    {
    	char arr1[] = "ABABAA";
    	char arr2[] = "ABAA";
    	def(arr1, arr2);
    	next(arr1, arr2);
    	kmp_search(arr1, arr2);
    		int build_next(arr1, arr2);
    		//函数声明
    	return 0;
    }

    这里KMP算法基本上已经写完了,不得不说,next数组的算法时最难的,需要理解主串与子串的走向,以及它的变化规律。还要避免重复的运算。

  • 可能KMP算法比较难以理解,但总之,只要知道其中的原理,就能很好的写出算法。你学会了吗?如果我有写的不对的地方,请你们指出来哦。我会虚心接受的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值