程序员

KMP算法的实现(超详解)

作者:admin 2021-08-06 我要评论

KMP算法的实现 要求解KMP算法首先要得到next数组也有的地方叫prefix数组。 先用手算的方法求得 。 假设模式串为abacab; 第一步列出模式串的所有前缀包括自身 1a ...

在说正事之前,我要推荐一个福利:你还在原价购买阿里云、腾讯云、华为云服务器吗?那太亏啦!来这里,新购、升级、续费都打折,能够为您省60%的钱呢!2核4G企业级云服务器低至69元/年,点击进去看看吧>>>)

KMP算法的实现

要求解KMP算法,首先要得到next数组,也有的地方叫prefix数组。
先用手算的方法求得
假设模式串为abacab;
第一步列出模式串的所有前缀(包括自身):
(1)a
(2)ab
(3)aba
(4)abac
(5)abaca
(6)abacab
第二步将各个前缀看作一个独立的字符串,依次求得各个字符串的最长前后缀的长度,这里的前后缀是不包括自身的(前缀不包括最后一个字符,后缀不包括第一个字符):
具体来看第一个字符串只有一个字符,因此它的前缀后缀都为空,所以它的最长公共前后缀的长度为0;
第二个字符串它的最长前缀为a,最长后缀为b,无公共部分,所以最长公共前后缀的长度为0;
第三个字符串最长前缀为ab,最长后缀为ba,不相等,减少前后缀的长度,前缀只剩下a,后缀也为a,因此长度为1;
第四个字符串它的最长前缀为aba,最长后缀为bac,前后缀不相等,减少前后缀的长度,前缀变为ab,后缀变为ac。前后缀不相等,继续减少前后缀的长度,前缀为a,后缀为c,不相等,所以长度为0;
五六同理,五的最长公共前后缀为a,长度为1;六的最长公共前后缀为ab,长度为2;
(1)a 0
(2)ab 0
(3)aba 1
(4)abac 0
(5)abaca 1
(6)abacab 2
求得的长度就是prefix数组的各个元素的值;
在这里插入图片描述
也可以直接将prefix数组的值全部右移一位,然后将prefix[0]的值设为-1;
下面考虑如何用代码来实现;
先观察第五个字符串的最长前后缀长度为1,第六个字符串的最长前后缀长度为2;第六个字符串比第五个字符串多了一个字符b,而第五个字符串的的第二个字符刚好为b。所以后一个字符串的长度和前一个字符串是有关系的,当前一个字符串的最长公共字符串的长度+1的那个字符和此时的字符串最后一个字符相等时,那么当前字符串的长度应该是前一个字符串的最长公共前后缀的长度+1。
定义两个变量i和len;
i指向当前字符串的最后一个位置;
len指向前一个字符串的最长公共字符串的长度+1的位置,因为数组的下标是从0开始的;所以len的值应该为前一个字符串的prefix的值+1-1,即前一个字符串的prefix的值;
初始化prefix[0]=0(任何情况下都为0,因为只有一个字符时无前缀和后缀);
i=1(指向第二个字符串的最后一个位置);
len=0;
如果p[i]==p[len],那么len++;prefix[i]=len;(当前字符串的长度比前一个字符串的长度多1);
如果p[i]!=p[len]时,这也是最难理解的地方,在这里插入图片描述

如图所示,此时p[len]!=p[i],图中的1、2部分为前面字符串的最长公共前后缀。要使当前字符串有公共前后缀,那么图中的圆圈所示部分应该相等,在这里插入图片描述
又因为第1,2部分的字符串是相等的,所以第一部分的两个圆圈的内容是相等的,且第一个圆圈的最后一个字符为i所指的字符。在这里插入图片描述
所以当p[len]!=p[i]时,len=prefix[len-1](len指向前一个字符的最长公共前后缀的下一个位置如图一所示,prefix[[len-1]指向第一部分的最长公共前后缀的下一个位置,也就是第一部分第一个圆圈的最后一个位置)接下来的p[i]==p[len]的判断会判断第一部分第一个圆圈的最后一个位置和当前i所指的位置的值是否相等,如果相等,那么prefix[i]的值应该为第一部分的最长公共前后缀的值+1,第一部分的最长公共前后缀的长度为prefix[i-1]的值也就是当前len的值。即len++;prefix[i]=len;如果判断p[i]==p[len]的结果为false,那么继续在第一部分的第一个圆圈内递归查找,比较第一个圆圈内的最长公共前后缀加一的位置和i所指的位置是否相等。说了这么多,应该不难理解了吧。下面看看代码。

void prefix_table(char p[],int prefix[],int n){
	prefix[0]=0;
	int len=0;
	int i=1;
	while(i<n){
		if(p[i]==p[len]){
			len++;
			prefix[i]=len;
			i++;
		}else{
			if(len>0){/*当len=0时,len-1的值为-1,数组下标越界*/ 
				len=prefix[len-1];
			}else{
				prefix[i]=0;/*当递归查找到第一个字符时,若果还不相等,那么这个字符串的最长公共前后缀的长度肯定为0*/ 
				i++;
			}
		}
	}
}

将prefix数组右移一位(kmp算法指针回溯的位置为前一个字符串的最长公共前后缀的长度所指的位置)

void move_prefix_table(int prefix[],int n){
	int i;
	for(i=n-1;i>0;i--){
		prefix[i]=prefix[i-1];
	}
	prefix[0]=-1;
}

查找字符串

void search(char text[],char p[]){
	int n=strlen(p);
	int *prefix=(int *)malloc(sizeof(int)*n);
	prefix_table(p,prefix,n);
	move_prefix_table(prefix,n);
	int m=strlen(text);
	int i,j;
	while(i<m&&j<n){
		if(j==-1||text[i]==p[j]){
			i++;
			j++;
		}
		else{
			j=prefix[j];
		} 
	}
	if(j>n){
		printf("Found\n");
	}
	else{
		printf("Notfound\n");
	}
	free(prefix);
	
} 
;原文链接:https://blog.csdn.net/qq_49302425/article/details/115877077

版权声明:本文转载自网络,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。本站转载出于传播更多优秀技术知识之目的,如有侵权请联系QQ/微信:153890879删除

相关文章
  • KMP算法的实现(超详解)

    KMP算法的实现(超详解)

  • C语言-10(学习C语言的第十天)

    C语言-10(学习C语言的第十天)

  • 做了两年多的安卓,突然想转Java后端,

    做了两年多的安卓,突然想转Java后端,

  • 程序员真的可以轻松月收入破万吗?看了

    程序员真的可以轻松月收入破万吗?看了

腾讯云代理商
海外云服务器