您的位置:首页 > 其它

自己关于KMP算法的理解

2014-11-10 08:07 281 查看
数据结构学到KMP,刚看到时也是弄了好长时间没有看懂,琢磨四天后问了一位大神,终于有点眉目了,理解之后自己又写出求next数组的方法,然后拉了一道题出来练练,过了,觉得好不容易弄懂,怕以后又忘掉,同时也检验一下自己是否真的理解,还是写下点东西记录一下吧。
感谢那位大神的同时还是感谢网上很多博客的,有一篇给我的帮助还是很大的,浅显易懂,如果有不明白的可以去看一下试试http://blog.csdn.net/yearn520/article/details/6729426
我就不像网上的博客引出传统暴力匹配的方法再介绍KMP,就直接从next数组开始吧。
next数组功能,举个例子

模式串
a
b
a
a
b
c
a
c
next值
0
1
1
2
2
3
1
2
(默认下标从1开始)
我们将模式串进行匹配,如果第一位 a 就不符合,这个需要特殊处理就直接将整个字符串向后移一位就好;
(至于为什么是0,我不懂,因为下标已经是从1开始,有待考究)
如果第二位不符,说明第一位已经符合,我们看b的next值,为1,就将第一位的 a 向后移至第二位,再进行比较。(说不定原串中就从第二位开始就与模式串匹配上了)
第三位不符的话,next值还是1,我们就再将这个串的第1位移到不符的位置;(其实这个串,第一位和第三位都是a,把第一位移过来明显还是不符,所以算是一种浪费,可能在下面的nextval中会优化)
第四位不符,说明前三位符合,然而第三位与第一位相等,又是a,那就直接将第二位移到原来第四位的位置进行匹配。
第五位不符,同上;
第六位不符,说明 第四第五位 a b 都匹配上了,next 值为 3,那就把第三位移到原来的第六位;
第七位不符,同第三位;
第八位不符,同第四位;

上面写的功能中隐约也说明了next数组的来历,我们来看看它是怎么求的

第1位是0
第j位,j != 1,就是从第 1 位到第 j-1 位比较字串,前缀和后缀相等的最大长度再加 1 ,这里的长度不能大于j-2,也就是前缀和后缀要为前 j-1 位的真字串
比如
a b a b
虽然前三位是对称的,第四位的next值是 2 而不是 4 ,如果比到第四位出错,我们只能将第二位移过去。
其它情况,匹配不上只能将串的头再移到不匹配的地方了。

人工求倒是很好求,写入程序呢,一直调用substr来判断相等效率会很低,自然有更好的方法
这里就用到了我给的那位大神的博客中的讲解。再次致敬
然后呢,我主要说一下我对子对称的理解
就拿那个大神的例子
(a g c t a g c )( a g c t a g c) tg
对于next的理解不同,他求的next逐位+1右移便是我们讲的next

常规来看,第15位next 是8 ,第16 位按照累加的求法应该是1 吧,但是为什么是 5 呢?
我们知道原理来说,g之前 agct 与开头的 agct 相等构成对称,确实是5
然而,我们算到 g 的时候,我们知道应该有一串长度为n 的字串构成对称条件,就算没有,n = 0 ,g 的next 值也总是 n+1
对于前面的 t ,next 值为 8,意味着之前有7位构成对称,也就是前7位与之后7位相同;
而我们要求的 长度为n的字串除了最后一位 t 之外,之前的 n-1 位也是上面那 7 位的后 n-1 位,既然前7位与之后7位相同,这n-1位也是前7位的后 n-1 位;
所以,我们让 i = next [ i ],访问到 t 的next值,缩短比较的距离。另外判断 模式串 的next[i]位和第 i 位是否相等,(第一次肯定是不等的),不等的话就在进行
i = next [ i ],为什么呢,相当于递归了。
每次i = next [ i ]的操作都能保证那 n – 1位相等(方便叙述我直接用 n-1 实际上 n的值到最后匹配上时才可以确定 ),我们只是一直在判断 t 是否能被匹配上,当匹配上时,第16位的next 就是匹配上的 t 的位置。
所以我们可以确定这种按照自己理解写出的算法:
void getnext( string s , int next[] )
{
	s = '#' + s; //为了使下标从1开始 
	int len = s.size();
	next[1] = 0;
	next[2] = 1;
	int i = 3 ;
	int j = 1 ;
	
	while ( i < len )
	{
		if ( s[i - 1] == s[j] )
		{
			next[i] = j + 1;
			i ++ ;
			j ++ ;
		}
		else 
		{
			j = next [j] ;
			if ( j == 0)
			{
				j ++ ;
				next[i] = j ;
				i ++ ;
			}
		}
	}
	
}
那么匹配的过程也就是

int kmp(string s1,string s2,int next[])
{
	s1 = '#' + s1; // s1 对应 next 数组 
	s2 = '#' + s2;
	int len1 = s1.size();
	int len2 = s2.size();
	int i = 1 ;
	int j = 1 ;
	while( j < len2 )
	{
		if ( s1[i] == s2[j] )
		{
			i ++ ;
			j ++ ;
		}
		else
		{
			if(next[i] != 0 )
				i = next[i];
			else
				i = 1;
			//i = next[i];
		}
		if ( i == len1)
		{
			return j - len1 + 1;
			break;
		}
	} 
	
}


that is it
有空我还会总结nextval
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: