关于KMP算法的总结

查阅大佬博客https://blog.csdn.net/v_july_v/article/details/7041827

 

1.kmp是什么

          kmp是一个可以让文本串不用回溯的字符串算法,什么意思呢?好比文本串”BBC ABCDAB ABCDABCDABDE“,模式串”ABCDABD“,他们从第一个开始匹配,i指向文本串当前匹配的字符位置,不用回溯的意思是文本串的i可以从0一直走到22,而不用返回。这也是kmp的精妙之处。

那么他是怎么做到可以让文本串指针不用回溯呢?

第一轮匹配

BBC   ABCDAB   ABCDABCDABDE

ABCDABD   ->向后匹配

BBC   ABCDAB   ABCDABCDABDE       i=11

           ABCDABD                                      j=7

 到此时模式串中D与文本串中的空格不匹配了,我们想一下蛮力算法此时会怎么做

BBC   ABCDAB   ABCDABCDABDE        i=6

             ABCDABD                                     j=1

我们可以看到,蛮力算法让i从11,回溯到6(该字符串是从1开始算起),我们现在来看kmp算法的匹配方式

BBC   ABCDAB   ABCDABCDABDE           i=11

                     ABCDABD                                j=3

 

i没有进行回溯,而是将j做了改变。因为当D和空格不匹配的时候,与D相邻的AB已经匹配成功,所以我们寻找模式串中在此AB之前是否还有AB可以与此AB匹配,以减少中间很多的不必要匹配。 这在做较长的匹配时,效率将提升很多。那么如何实现只动模式串而不让文本串回溯呢?!

这里将出现kmp算法中最重要的一个数组  next数组,当匹配不成功时,只用将指针指向该指针对应的next数组就ok了。

这里先给出kmp算法的实现,next数组求法在下面给出


int KmpSearch(char* s, char* p)
{
	int i = 0;
	int j = 0;
	int sLen = strlen(s);
	int pLen = strlen(p);
	while (i < sLen && j < pLen)
	{
		//如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),向前继续匹配
		if (j == -1 || s[i] == p[j])
		{
			i++;
			j++;
		}
		else
		{
			//如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]    
			//next[j]即为j所对应的next值      
			j = next[j];
		}
	}
	if (j == pLen)
		return i - j;
	else
		return -1;
}

 

 

2.什么是next数组

next数组是仅对模式串来说的,可以看作是模式串的自我匹配。next数组是在当前字符前的字符串中,前缀字符串与后缀字符串的最大匹配长度。这句会可能不是很好理解,我们来举个例子,先说什么是前缀字符串和后缀字符串

         ABCDABD   

1>       什么是前后缀字符串:

模式串"ABCDABD"中 ”A","AB","ABC",,,,,,,只要从第一个字符  “A"开始数的子符串都叫做前缀字符串

那么后缀字符串就好理解了,就是从要数的字符往前数任意字符数,就叫做后缀字符串

2>        什么叫最大前后缀字符串匹配数

还是上面的字符串,我们现在要求第二个字符‘D'前最大前后缀字符串的匹配长度,我们可以看到’D'之前的匹配的前后缀字符串只有”AB".所以next[D]=2(这里的D是第二个D),我们用下标表示是不是就是next[6]=2(下表从0开始)。那我们是不是可以直接用next[6]表示第6个字符下一个跳转的位置呢?我们看next[6]=2,下标为2的字符是C,现在是不是和上文kmp的匹配方式相同了呢,当D不匹配时,换做C进行匹配。为什么这样是正确的呢?我们知道next数组表示的最大匹配长度,那这个长度是不是就是前缀字符串的长度,而我们要做的就是跳转的就是前缀字符串的后一个字符进行比较,又因为字符串下标都是从第0位开始的,前缀字符串是从第一个开始的,所以前缀字符串的长度是不是就表示当前字符要跳转的目标字符。

void getnext(char s[]){
    int i=0,j=-1;
    next[0]=-1;
    while (i<len){
        //len表示模式串长度
        if(j == -1 || s[i]==s[j])
        //如果相等或者是第一个字符,就对他下一个位置进行复制
            next[++i]=++j;
        else j=next[j];
        //否则向前跳转,直到找到相同的为止
    }
}

建议多看几道题加深理解

如有错误,欢迎指正

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值