自己整理的KMP算法

以下是自己整理的KMP算法,更厉害更全面的KMP算法可参见大牛们的博客。

大牛1:http://blog.csdn.net/v_july_v/article/details/7041827#

大牛2:http://blog.csdn.net/yaochunnian/article/details/7059486

1、普通字符串匹配BF算法与KMP算法的时间复杂度比较

    KMP算法是一种线性时间复杂的字符串匹配算法,它是对BF算法(Brute-Force,最基本的字符串匹配算法的)改进。对于给的原始串S模式串P,令|S|=n,|P|=m,需要从字符串S中找到字符串P首次出现的位置的索引。

BF算法的时间复杂度O(n*m),空间复杂度O(1)。

KMP算法的时间复杂度O(m+n),空间复杂度O(m),因为KMP算法需要预处理模式串。具体说明,见文章最后。

2、KMP算法思想
    普通的字符串匹配算法(如(BF)必须要回溯。
但回溯就影响了效率,回溯是由P串本身的性质决定的,是因为P串本身有前后“部分匹配”的性质。像上面所说如果主串为abcdef这样的,大没有回溯的必要。
    改进的地方也就是这里,我们从P串本身出发,事先就找准了P自身前后部分匹配的位置,那就可以改进算法。
    如果不用回溯,那模式串下一个位置从哪里开始呢?
    还是上面那个例子,P(模式串)为ababc,如果c失配,那就可以往前移到aba最后一个a的位置,像这样:
...ababd...
   ababc
    ->ababc
这样i不用回溯,j跳到前2个位置,继续匹配的过程,这就是KMP算法所在。这个当P[j]失配后,j 应该往前跳的值就是j的next值(模式串P向右滑动的值),它是由P串本身固有决定的,与S串(主串)无关。

3、next数组的含义
   重点来了。下面解释一下next数组的含义,这个也是KMP算法中比较不好理解的一点。令原始串为: S[i],其中0<=i<=n;模式串为: P[j],其中0<=j<=m。nextval数组也称k数组,用于指导模式串中使用几号元素进行后续的匹配。

下面给出nextval数组的get_nextval函数的正确代码:(综合了一些作者的实现,也许跟其他方法有些不同)

void get_nextval(const char * p, const int plen, int *nextval)
{
   int i=0;
   nextval[i]=-1;
   int j=-1;
   while (i<plen-1)
   {
     if (-1==j || p[i]==p[j])//循环的if部分
      {
      ++i;
      ++j;
      nextval[i]=j;
      } 
      else //循环的else部分
        j=nextval[j];
   }
   return;
}

如:模式P=“ababc”,使用get_nextval函数后所得的nextval为:nextval=“-1 0 0 1 2”

4、利用求得的next数组各值运用Kmp算法
 Ok,next数组各值已经求得,万事俱备,东风也不欠了。接下来,咱们就要应用求得的next值,应用KMP算法来匹配字符串了。还记得KMP算法是怎么一回事吗?容我再次引用下之前的KMP算法的代码,如下:

int kmp_search(const char *src, const int slen, const char *patt, const int plen, const int *nextval, int pos)
{
	//输入:src, slen主串
	//输入:patn, plen模式串    
	//输入:nextval, KMP算法中的next函数值数组   
	
	int i=pos;
	int j=0;
	while(i<slen && j<plen)
	{
		if(j==-1 || src[i] == patt[j])
		{
			++i;
			++j;
		}
		else
		{
			j=nextval[j];
			//当匹配失败的时候直接用p[j_next]与s[i]比较
			//下面阐述怎么求这个值,即匹配失效后下一次匹配的位置
		}
	}
	if(j>=plen)
		return i-plen;
	else
		return -1;
}


5、KMP算法的具体实现
根据上文中的解析,完整写出KMP算法的代码已经不是难事了,整理如下:

#include <iostream>
using namespace std;

void get_nextval(const char * p, const int plen, int *nextval)
{
	int i=0;
	nextval[i]=-1;
	int j=-1;
	while (i<plen-1)
	{
		if (-1==j || p[i]==p[j])		//循环的if部分
		{
			++i;
			++j;
			nextval[i]=j;//改进前
			
			/*
			if(p[i]!=p[j])//改进后
				nextval[i]=j;
			else
				nextval[i]=nextval[j];
			*/
		} 
		else					//循环的else部分
			j=nextval[j];
	}
	return;
}

int kmp_search(const char *src, const int slen, const char *patt, const int plen, const int *nextval, int pos)
{
	//输入:src, slen主串
	//输入:patn, plen模式串    
	//输入:nextval, KMP算法中的next函数值数组   
	
	int i=pos;
	int j=0;
	while(i<slen && j<plen)
	{
		if(j==-1 || src[i] == patt[j])
		{
			++i;
			++j;
		}
		else
		{
			j=nextval[j];
			//当匹配失败的时候直接用p[j_next]与s[i]比较
			//下面阐述怎么求这个值,即匹配失效后下一次匹配的位置
		}
	}
	if(j>=plen)
		return i-plen;
	else
		return -1;
}

int main()
{
	/*
	char *src="aabababcababcd";
	char *patt="ababc";
	*/
	char *src="baba";
	char *patt="ababc";
	/*
	char *src = "aabcabcebafabcabceabcaefabcacdabcab";
	char *patt = "abce";
	*/
	  const int slen=strlen(src);
	const int plen=strlen(patt);
	
	int *nextval=new int[plen];
	
	get_nextval(patt, plen, nextval);
	
	
	cout<<"nextval:"<<endl;
	for(int i=0;i<plen;++i)
		cout<<nextval[i]<<" ";
	cout<<endl;

	int index = kmp_search(src, slen, patt, plen, nextval, 0);
	if(index!=-1)
		cout<<"The first index of patt is: "<<index<<endl;
	else
		cout<<"not found matching!"<<endl;
	delete []nextval;
	
	return 0;
}

注:再次分析,KMP算法的时间复杂度O(m+n),当n>>m时,可视为θ(n),空间复杂度为θ(m)。具体分析见《算法导论》。

继续努力,O(∩_∩)O哈哈~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值