经典算法之浅析kmp算法

         KMP算法,也就是字符串匹配算法,是很多场合都会用到的算法,这个算法想要解决什么样的问题呢?举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串"ABCDABD"? 
        传统的求解,我想很多人都已经想到了,遍历源字符串的每一位嘛,依次和要匹配的字符串比较,code很easy,但效率较为低下,可以想到,时间复杂度为O(N*M),那有没有更好的算法解决这类问题呢?
       KMP算法就是能以更快的效率去解决这类问题,它的时间复杂度能降低到O(N+M),是不是觉得很厉害?  确实很厉害,以至于很多人对这个算法都理解不了,本文将尽量用最浅显的语言,来试图将KMP讲述清楚。

       

      就像我最开始说的,我们马上能想到的解决方式就是遍历源字符串的每一位,依次和匹配字符串比较,那么相对于这种方法,KMP究竟快在哪里呢?  其实整个KMP优化的核心就是   当匹配失败时,我是直接i++移动到下一位继续匹配,还是移动到我通过某种算法得出来的位置?(这个位置是最优解) 这是什么意思呢?    举个例子。

       对于这样的一种情况:

当匹配失败时,我该从下一个index B开始匹配呢,还是 可以通过验证,中间的某一段位置都可以确定匹配不了目的字符串,而从后面的某个位置开始匹配呢。
传统的做法:

KMP的做法:

       KMP就是通过这样的一种思路,来加速整个匹配过程的,于是整个核心 就变成了,当匹配失败时该如何找到下次对应的匹配位置? 在KMP算法里,是通过next数组来保存这一信息的。因此下面我们就来讲一讲如何求出这个next数组。

     提到next数组,就不得不要了解两个概念:"前缀"和"后缀"。 "前缀"指除了最后一个字符以外,一个字符串的全部头部组合;"后缀"指除了第一个字符以外,一个字符串的全部尾部组合。而next数组保存的就是最长前缀和后缀相等的长度值。

     先不管怎么求的next数组,假设我们已经有了next数组,在kmp中是如何通过next数组,来实现匹配的加速呢?下面直接上代码,对着code理解:

int kmp_code(string str1,string str2)
{
	int len1=str1.length();
	int len2=str2.length();
	int i=0,j=0;
	//字符串为空或目标字符串长度大于源串。
	if((str1 == NULL)||(str2 == NULL)||(len1<len2))
		return -1;
	int* next = GetNextArray(str2);
	//循环条件,index值小于各自str长度
	while(i<len1 && j<len2)
	{
		//字符匹配,则各自++
		if(str1[i] == str2[j])
		{
			i++;
			j++;
		}
		else if(next[j] == -1)    //没有前缀后缀相等的值。
			i++;
		else                     //从next数组里得到index值
			j=next[j];
	}
	return j == len2? i-j: -1;    //返回值 
}

不管next怎么求的,代码是不是非常的简洁?,下一步就要讲解如何求next数组了,即最长的前缀与后缀相等的值,这里我们规定:next[0]=-1;next[1]=0;

int* GetNextArray(string str)
{
	int *next =new int[str.length()];
	if(str.length() == 1)
		return  next[0] = -1;
	next[0] =-1;
	next[1] =0;
	int index = 2      //next下标
	int pos = 0;      // 对应的next值
	while(index < str.length())
	{
		if(str[index-1] == ms[pos])     //字符相等,则next值+1
			next[index++] = ++pos;
		else if(pos >0)
			pos =next[pos];    //推到上一个next值,
		else
			next[pos++] = 0;   //往前推的next值到0都找不到,则当前next值为0;
	}
	
	return next;
		
}

求next数组,可以说是整个KMP的核心了,如何快速求出next数组呢?   其实整个代码的思想,就是利用前面已经求好的next数组,来求当前index下的next数组。整个思路如下:
1.判断当前index字符与前者(记录好的)next数组对应的下标值(pos)的字符相等?,相等则pos值+1,为当前index的next值

2.不等,则往前推pos对应的next值,继续上述比较。

3.如果推到底,即 next值为0,则将当前index下的next值记录为0;

 

      可以说,kmp的每一步都设计的非常巧妙,利用next数组,来加速匹配的过程,而next数组的求法也不是简单的暴力遍历解决,而是利用前面已经求好的next数组,来求解后面的next数组,可以说是相当的巧妙了。如今的计算机中,字符串匹配是无时无刻都在做的一件事,可以说,kmp是一个非常常用,也非常重要的一个算法。

   

 注:本文讲解的比较浅显,因为kmp算法,确实不好理解,也不是一下就能理解会的东西,需要大家慢慢消化,而将kmp能说清楚,也不是一件容易的事情。代码虽短,却无时无刻体现着人类智慧的结晶

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值