KMP算法

1,关于模式串目标串的匹配过程,话不多说,先上图

KMP算法_第1张图片

上图中O为目标串,f为模式串,A,B为模式串中最长的可匹配的前缀,后缀。在i之前模式串与目标串是匹配的,i为失配位置。我们知道在模式串中B之前均与A部分不匹配,而这些部分都已与目标串匹配,故A部分不可能与B部分对齐的目标串的前面部分匹配,所以我们可以直接将模式串的A部分移到与目标串中与B部分匹配的部分。(即,目标串可以从匹配失败的地方开始重新匹配,而不必像朴素算法回退到与模式串匹配的第二个位置。模式串也可以根据前后缀的记录从前缀的后一个位置开始匹配)这就是kmp算法高效的原因。


2,有了前面的说明,我们知道记录模式串的模式(即该串中子串的重复情况(个人是这么理解的))是十分重要的。在此,我们引入一个next数组来记录。

show   the   code:


void getnext()
{
    int i,j;
    next[0]=0;
    for(i=1,j=0;s[i];i++){///j为next[i-1]
    	while(j>0&&s[i]!=s[j]){///寻找前缀
    		j=next[j-1];
		}
		if(s[i]==s[j])++j;///若与前缀匹配,则匹配长度+1
		next[i]=j;
	}
}

用一个例子来说明一下代码吧:

串:    a  b  c  d  e  f  g  a  b  c  d    a    b    c    e

下标:0  1  2  3  4  5 6  7  8  9  10  11  12  13  14

next: 0  0  0  0  0 0 0  1  2  3   4   1    2    3    0


我们从11号下标对应的位置来看,通过10号下标的next值我们找到了3号下标(在此之前j值为4,通过while循环,j值变为0)(因为next数组记录的是与前缀的匹配长度,而我们的数组下标是从0开始的,所以next数组在j-1位置的值即为串中需要查看是否与j位置的字符匹配的字符的下标),查看0号字符与11号字符是否一致,若一致则后缀长度+1.


kmp算法:


int kmp(char *w,char *t){
	int l1,l2,i,j,sum=0;
	l1=strlen(w);
	l2=strlen(t);
	for(i=0,j=0;i<l2;i++){
		if(l2-i<l1-j)break;
		while(j>0&&w[j]!=t[i]){
			j=next[j-1];
		}
		if(w[j]==t[i])++j;
		if(j==l1){
			sum++;
			j=next[j-1];
			continue;
		}
	}
	return sum;
}

你可能感兴趣的:(算法)