KMP算法详解与实现和next数组的代码化总结

KMP2年前反复看的时候,对next数组和基本原理,记一遍忘一遍,最近要用到字符串匹配得问题,要造轮子的时候又想到它了,这次好好整理一遍,要再次深刻的理解一遍。

KMP–简介

kmp是由Knuth-Morris-Pratt三位发明者共同命名的

传统匹配

EFEFEVDDCSAFFFEFERFTEWV
当我们想让FEFER这个pattern字符串去匹配上面的text字符串时,我们通常的做法是尝试是回朔算法。
PS: 回溯算法是一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回。
第一步:匹配不正确在下一个字符重新开始
在这里插入图片描述
第二步:匹配成功继续对FEFER这个pattern字符串进行后续的验证
在这里插入图片描述
第三步:直到我们发现最后一个字符不一致,此次从第二个字符开始匹配的方式失败,
在这里插入图片描述
第四步:回朔,从第三个字符重新开始,失败,继续对第四个字符重新开始匹配,直至匹配成功。
在这里插入图片描述
这样的方式不难猜出,当被匹配的text字符串长度为N,匹配得pattern字符串长度为M,那么时间复杂度达到O(N*M)

KMP优化

EFEFEVDDCSAFFFEFERFTEWV
同样当我们想让FEFER这个字符串去匹配上面的字符串时,我们这次尝试的算法为KMP算法。
第一步:匹配不正确在下一个字符重新开始
在这里插入图片描述
第二步:匹配成功继续对FEFER字符串进行后续的验证
在这里插入图片描述
第三步:直到我们发现最后一个字符不一致,从这里开始发生改变,我们无需回到第二个字符开始
在这里插入图片描述
提取FEFE这个已匹配字符数
我们去寻找这个FEFER他的子串的前后缀最大公共子串

  • F的前后缀子串都为空,相同子串为0
  • FE的前后缀子串分别为{F} | {E},最大相同子串为0
  • FEF的前后缀子串分别为{F},{FE} | {F},{EF},最大相同子串长度为1
  • FEFE的前后缀子串分别为{F},{FE},{FEF} | {E},{FE},{EFE},最大相同子串长度为2
  • FEFER的前后缀子串分别为{F},{FE},{FEF},{FEFE} | {R},{ER},{FER},{EFER},最大相同子串长度为0

计算下次应该在哪个位置重新开始匹配
移动距离=已匹配成功字符长度-最大相同子串长度
第四步:我们经过计算需要移动2=4-2个单位,所以我们直接从第四个字符开始匹配,省去了一些匹配次数
在这里插入图片描述
在这里插入图片描述

第五步:同理我们经过计算需要移动2=2-0个单位,所以我们直接从第6个字符开始匹配,省去了一些匹配次数,反复如此,直到匹配成功
提取FE这个已匹配字符数

  • F的前后缀子串都为空,相同子串为0
  • FE的前后缀子串分别为{F} | {E},最大相同子串为0
    在这里插入图片描述
    这样的方式不难猜出,当被匹配的字符串pattern长度为N,匹配得字符串长度为M,那么时间复杂度达到O(N+M),当然如果没有出现类似这种匹配字串的重复现象,他又会退回成回朔算法。

next数组的求法

我们应用到编程中,最难实现的点是如何根据待匹配的pattern字符串求出对应每一位的最大相同前后缀的长度
在程序中,我们可以将其转化为对next数组的求解
next数组next[k]本质是对pattern字符串的子串长度为k+1时最大相同前后缀子串长度
例如abab:如果列出它最大前后缀子串长度表(此处所说子串不包含本身字符串)

k=0 | a k=1 | ab k=2 | aba k=3 | abab
前缀子串(s[0~k]) 空集 {a} {a}{ab} {a}{ab}{aba}
后缀子串(s[(3-k)~3]) 空集 {b} {a}{ba} {a}{ab}{bab}
相同前后缀子串长度 0` 0 1 2
next[0] next[1] next[2] next[3]
next数组 -1 -1 0 [a的下标] 1 [b的下标]

由此可以得出:
next[k]是s[0~k]字符串的最长相等前后缀的前缀最后一位的下标
以此来更好的将其代码化:
了解到这里我们将抽象的语言已经转化为代码的语言,接下来实现的方式我们选择递推,通过已知的next[0]~next[k-1]来推出next[k]

public void getNext(char s[], int len){
	int j = -1;
	next[0] = -1;
	for(int i = 1; i < len; i++){
		while(j != -1 && s[i] != s[j + 1]){
			j = next[j];
		}
		if(s[i] == s[j + 1]){
			j++;
		}
		next[i] = j;
	}
}

猜你喜欢

转载自blog.csdn.net/qq_35050438/article/details/106114465