KMP算法详解及其Java实现
2021-07-04 11:05
标签:fonts 思路 例子 复习 分享图片 前缀和 mat void 大量 KMP算法,又称作“看猫片”算法(误),是一种改进的字符串模式匹配算法,可以在O(n+m)的时间复杂度以内完成字符串的匹配操作,其核心思想在于:当一趟匹配过程中出现字符不匹配时,不需要回溯主串的指针,而是利用已经得到的“部分匹配”,将模式串尽可能多地向右“滑动”一段距离,然后继续比较。 求一个字符串(模式串)在另一个字符串(主串)中的位置,称为字符串模式匹配。 在朴素的字符串模式匹配算法中,我们对主串S和模式串T分别设置指针i和j,假设字符串下标从0开始,初始时i和j分别指向每个串的第0个位置。在第n趟匹配开始时,i指向主串S中的第n-1个位置,j指向模式串T的第0个位置,然后逐个向后比较。若T中的每一个字符都与S中的字符相等,则称匹配成功,否则,当遇到某个字符不相等时,i重新指向S的第n个位置,j重新指向T的第0个位置,继续进行第n+1趟匹配。 例如,我们对模式串T=“abaabcac”和主串S=“abcabaabaabcacb”进行匹配。如图1.1,此时正在进行第4趟匹配,S[3...7]与T[0...4]均相等,但当i=8,j=5时,S[8]与T[5]不相等,匹配失败。于是,置i=4,j=0,相当于将模式串向右移动一位后,重新开始下一趟匹配,如图1.2。 在上面的例子中,我们可以看到,当i=8,j=5时,S[8]与T[5]不相等,于是置i=4,j=0,相当于将模式串向右移动一位,再开始下一趟匹配。然而,通过观察我们可以发现,之后的两趟匹配,即i=4,j=0以及i=5,j=0都是不必要的。这是因为,在之前的一趟匹配过程中,我们已经部分匹配了T的子串“abaab”。此时将T向右移动一位,则相当于对T中的“abaab……”与S中的“baab……”进行匹配,显然无法匹配成功。继续右移T,则相当于对T中的“abaab……”与S中的“aab……”进行匹配,依然无法匹配成功。只有当T向右移动3位后,此时对T中的“abaab……”与S中的“ab……”进行匹配,才会有成功的可能,也就有必要向后继续进行比较。如图2.1。 这就是KMP算法的基本思路。对于模式串T中的前j个字符组成的子串,设置数组next[j]存放一个值,当模式串T匹配至第j个字符时与主串不相等,则i指针不变,将j指针置为next[j]的值,然后继续进行比较。在上例中,串“abaab”为模式串T的前5个字符组成的子串,令next[5]=2,当i=8,j=5时,S[8]与T[5]不相等,于是置i=8,j=next[j]=next[5]=2,然后继续进行比较。 因此,KMP算法的核心在于求出数组next,即模式串T中每一个长度为j (0 在求解next数组前,我们首先需要理解next数组的含义。回到前面的例子,当T的子串“abaab”的下一个字符与主串不相等时,主串的指针i不变,j回溯至2,指向T的第3个字符,其本质是因为串“abaab”的前缀和后缀有一个长度为2的最长公共串“ab”,因此我们省略了前缀“ab”和后缀“ab”的比较过程,直接对它们的后一个字符,即T[2]和S[8]进行比较。 再看另一个例子,假设有模式串T=“abacaabadad”,其已部分匹配完T[0...7],即“abacaaba”,在匹配T[8]时遇到匹配失败,因T[0...7]的前缀和后缀有长度为3的最长公共串“aba”,因此next[8]=3,置j=next[j]=next[8]=3,i不变,然后从T[3],即T的第4个字符开始比较。如图2.3。 总之,对于模式串T,next[j]代表了T的前j个字符组成的子串中,其前缀和后缀的最长公共串的长度。 求解字符串T的next数组的算法如下: 比较T[j-1]与T[k]的值, b. 若T[j-1]不等于T[k],令k=next[k],若k等于-1,则next[j]=0,否则跳至3。 下面以模式串T=“abaabcac”为例,给出求next数组的过程: 将next数组全部求出之后,只需在简单的匹配算法上稍作修改,便得到了KMP的匹配算法:当模式串T匹配至第j个字符时匹配失败,i指针不变,将j指针置为next[j]的值,若j的值为-1,则将i和j同时加1。随后继续进行逐个的比较。 下面以模式串T=“abaabcac”和主串S=“abcabaabaabcacb”进行匹配为例,给出KMP匹配算法的全过程。 以上就是KMP匹配算法的全过程。总结一下,KMP算法的实质就是以空间换时间,在匹配之前将模式串的一些信息存储起来(next数组),在随后的匹配过程中利用这些信息减少不必要的匹配次数,以提高匹配效率。在实际的应用过程中,简单模式匹配算法的执行时间常常接近于KMP算法,仅当主串与模式串有很多“部分匹配”时,KMP算法才能显著提升性能。 下面给出KMP算法的Java代码。整个算法分为两部分,一是next数组的求解,二是KMP匹配过程。 在学习KMP算法思路的过程中,我大量参考了“王道考研系列”的《数据结构联考复习指导》一书,以及CSDN博主v_JULY_v的文章:从头到尾彻底理解KMP,特此鸣谢。 同时感谢微博博主@回忆专用小马甲和实验室的大红袍CocoXu提供的大量猫片,让我在学习KMP算法的过程中拥有持续的动力。 文章中如有错误,欢迎指正! KMP算法详解及其Java实现 标签:fonts 思路 例子 复习 分享图片 前缀和 mat void 大量 原文地址:https://www.cnblogs.com/imzhr/p/9613963.html
1. 朴素的字符串模式匹配算法
利用此种方法进行字符串匹配,最坏情况下时间复杂度为O(n*m),其中n和m分别为主串和模式串的长度。
2. 改进的字符串模式匹配算法——KMP算法
因此,当i=8,j=5,T的子串“abaab”已经匹配成功,而其后一位字符却不相等时,不必回溯i指针,置i=8,j=2,继续向后比较,相当于将T向右移动3位,并从T的第3位开始向后比较。如图2.2。next数组求解算法
a. 若T[j-1]等于T[k],则next[j]=k+1。
之前已经求得模式串T的next数组为[-1, 0, 0, 1, 1, 2, 0, 1]。
3. KMP算法的Java实现
public class KMP {
/**
* 求出一个字符数组的next数组
* @param t 字符数组
* @return next数组
*/
public static int[] getNextArray(char[] t) {
int[] next = new int[t.length];
next[0] = -1;
next[1] = 0;
int k;
for (int j = 2; j
参考资料及致谢