简单的讲懂KMP算法(配图最细保姆级手把手教会!!)

简介: 简单的讲懂KMP算法(配图最细保姆级手把手教会!!)

一、前言


KMP 算法(Knuth-Morris-Pratt 算法)是一种高效的字符串匹配算法,他比较BF算法复杂度更低但是也更难理解,它的算法非常精妙减少了BF算法(暴力算法,上一篇博客有讲解)的无用重复操作大大减少了循环次数,再加上限制条件使得它的效率更加高效,我有看到许多大神在讲KMP算法但是大部分都是理论性讲述,不易懂打退了许多学者的热情以及自信心,我将会保姆级和大家分析KMP算法的思想以及代码的实现!!!

二、问题解决


KMP算法是解决问题的方法更是一种思想,它用来解决子串在母串出现位置以及匹配问题。但是这种思想我们一但学会对我们解决其他问题都会有启发的,下面我就不过多的说闲话了开始正题!!!

三、KMP算法思想以及相对BF算法的优势


它不同与BF算法(暴力解法),BF算法是在母串与子串中分别遍历,如果两个字符串的字符匹配成功位置都向后移动继续遍历,一旦失败就要将子串返回到0下标的位置,母串返回到第一个成功匹配的字符的下一位上(i=i-j+1,上一篇有细讲,后面关系不大)开始遍历,这样就可能会降低效率去重复遍历遍历过的内容,而KMP算法的思想是省略掉母串以及子串中没必要的重复匹配。

下面是BF算法的遍历方式的图解

image.png

编辑

image.png

编辑

这种方法遍历会使母串的寻找效率下降,每次都要回到匹配成功的下一个位置,不论你遍历了多少个字符,并且在子串的遍历效率也会下降

下面是KMP算法历方式的图解

我们用母串为“cabababc”,子串为“ababc”给大家做演示

红线是第一次匹配,黑线时第二次匹配,蓝线是第三次匹配,这里大概有个KMP匹配的逻辑就好,下面我要开始KMP的保姆教学了!!!

image.png

编辑

KMP算法从图中例子看出在第二次的匹配失败时其实已将将子串的前2个字符ab(与2,3位置的a,b正好重复)匹配了,所以我们没有必要重新遍历子串以及母串我们只需要在这个位置上继续进行匹配如果没有匹配成功母串的位置还是不返回而是指向下一个位置,因为前面的几个字符都是匹配失败了的没有必要再返回重新比较(细细思考下这句话!)比如匹配母串“abcdab”子串“bce”,这里母串匹配到d的下标发现匹配失败了,这就说明前面的几个字符都是失败的所以没必要和BF算法一样返回再进行没必要的重复匹配。子串也不是返回0的位置因为子串中总会有相同的字符出现,如果这个相同的几个字符开头字符是0下标的字符,就可以将子串中后面重复的字符出现匹配失败的字符返回到子串开头出现重复的字符的下一个位置去和母串再进行匹配,就像你在用钥匙开门你用不同的钥匙开了好几个门了,但是下面一个门的钥匙用错了打不开就要重新挑战比赛,这时候你都知道了前面有几关你已经闯过了只需要用相同的钥匙再开一边就好了,而不是再一个个权益使,这样可以提高效率,子串的next数组(第5部分讲解)就是这样一个记录子串返回的前面出现重复的位置,来提高效率

四、KMP算法大体的代码书写


我们先将两个字符串的长度得出,如果子串长于母串-起始位置的长度就肯定不会存在与母串中。

然后我们遍历子串和母串进行匹配大体和BF算法一样,但是不同的时循环中母串的返回位置以及子串的位置,母串位置不回退只会往前++进行匹配,子串匹配成功继续++匹配下一个字符,如果失败返回next数组中匹配失败字符的位置,最后出了循环后,比较是否子串被完全匹配也是subLength是否等于j来判断是否完全匹配来返回对应的位置。

代码如下:

 public static int KMP(String str,String sub,int pos) {
        int strLength=str.length();
        int subLength=sub.length();
        if(subLength>strLength-pos) return -1;
        int[] next=new int[subLength];
        getNext(sub,next);
        int i=pos;
        int j=0;
        while(i<strLength&&j<subLength) {
            //这里就是母串不会往回返只会勇往直前或者稍微停留
            //j==-1是子串第一个字符都与母串无法匹配所以母串向后走,子串走到0的位置
            if(j==-1||str.charAt(i)==sub.charAt(j)) {
                i++;
                j++;
              //这块代码等看了第五部分再看
              //这里的子串回到对应的重复位置上减少重复匹配次数
            } else {
                j=next[j];
            }
        }
        if(subLength==j) {
            return i-j;
        }
        return -1;
    }

五、next数组


next数组是存放子串从0位置开始连续的重复字符的长度的一个数组。当子串的某个位置匹配失败时直接返回到next对应的位置继续匹配就好。那next数组的值怎么定呢?

我们规定next[0]为-1,next[1]为0,你可能会想为什么这么定值,先跟着看到后面你就会感觉到这两个值定的有多妙了!2位置的a的前一个字符为b从子串的0位置开始没有连续的字符与他匹配,所以为0,3位置的b前一个字符时a,从子串0的位置看正好有一个a与其相同所以为1,4位置的c前两个字符ab与子串的0位置开始到1下标位置的ab两个字符相同所以为2

为什么是这样决定next的元素?

当子串匹配到c的时候失败的时候前面的abab都完全匹配了,c前面的2,3位置的ab是一定被匹配的。这时我们可以把0,1位置的ab作为这一次c匹配失败前匹配成功的ab然后再去尝试匹配后面的字符,这样就减少了从头开始一个个匹配的次数提高了效率,所以我们都是从匹配失败的前一个字符倒着看,并且从0下标开始看是否有连续相同的字符,把相同的字符数量作为下次子串匹配的开始位置!!!这就是next数组存在的原因与思想。

image.png

 public static void getNext(String sub,int[] next) {
        next[0]=-1;
        next[1]=0;
        int k=0;
        int i=2;
        int subLength=sub.length();
        while(i<subLength) {
            if(k==-1||sub.charAt(i-1)==sub.charAt(k)) {
                next[i]=k+1;
                i++;
                k++;
            } else {
                k=next[k];
            }
        }
}

这里当前一个字符和k对应的字符相同时说明匹配的字符又多了一个所以是next[i]=k+1了,k==-1时到了初始位置都没有匹配所以这个地方存0,这就是第一个值存放为-1和第二个存放为0的妙处!!!这里不好理解的就是k=next[k]了,这里就是一直回退,不相等时,将k移动一个周期串长度(k = next(k)),继续匹配如果一直匹配不到,k最终会变成-1,那说明k的周期串加上前缀串第一个字符,并不是前面出现的一个周期串,因此k要从头再开始匹配。

相关文章
|
1月前
|
算法
第四章 KMP算法理论基础
第四章 KMP算法理论基础
17 0
|
1月前
|
算法
KMP算法
KMP算法
23 0
|
3月前
|
算法 C++
A : DS串应用–KMP算法
这篇文章提供了KMP算法的C++实现,包括计算模式串的next数组和在主串中查找模式串位置的函数,用于演示KMP算法的基本应用。
|
4月前
|
数据采集 算法 JavaScript
揭开JavaScript字符串搜索的秘密:indexOf、includes与KMP算法
JavaScript字符串搜索涵盖`indexOf`、`includes`及KMP算法。`indexOf`返回子字符串位置,`includes`检查是否包含子字符串。KMP是高效的搜索算法,尤其适合长模式匹配。示例展示了如何在数据采集(如网页爬虫)中使用这些方法,结合代理IP进行安全搜索。代码示例中,搜索百度新闻结果并检测是否含有特定字符串。学习这些技术能提升编程效率和性能。
116 1
揭开JavaScript字符串搜索的秘密:indexOf、includes与KMP算法
|
3月前
|
算法
KMP算法
KMP算法
26 0
|
4月前
|
算法 Java
KMP算法详解及其在字符串匹配中的应用
KMP算法详解及其在字符串匹配中的应用
|
5月前
|
人工智能 算法 BI
一篇文章讲明白KMP算法(俗称看毛片算法)
一篇文章讲明白KMP算法(俗称看毛片算法)
55 0
|
20天前
|
算法 安全 数据安全/隐私保护
基于game-based算法的动态频谱访问matlab仿真
本算法展示了在认知无线电网络中,通过游戏理论优化动态频谱访问,提高频谱利用率和物理层安全性。程序运行效果包括负载因子、传输功率、信噪比对用户效用和保密率的影响分析。软件版本:Matlab 2022a。完整代码包含详细中文注释和操作视频。
|
4天前
|
算法 数据挖掘 数据安全/隐私保护
基于FCM模糊聚类算法的图像分割matlab仿真
本项目展示了基于模糊C均值(FCM)算法的图像分割技术。算法运行效果良好,无水印。使用MATLAB 2022a开发,提供完整代码及中文注释,附带操作步骤视频。FCM算法通过隶属度矩阵和聚类中心矩阵实现图像分割,适用于灰度和彩色图像,广泛应用于医学影像、遥感图像等领域。
|
6天前
|
算法 调度
基于遗传模拟退火混合优化算法的车间作业最优调度matlab仿真,输出甘特图
车间作业调度问题(JSSP)通过遗传算法(GA)和模拟退火算法(SA)优化多个作业在并行工作中心上的加工顺序和时间,以最小化总完成时间和机器闲置时间。MATLAB2022a版本运行测试,展示了有效性和可行性。核心程序采用作业列表表示法,结合遗传操作和模拟退火过程,提高算法性能。