KMP算法详解+动图演示

news2024/11/16 10:17:27

目录

一、KMP算法简介

二、KMP算法的详细图解

1. 先了解BF算法的基本思路 

2. 简单了解KMP算法 

3. next数组的引入

4. next数组的代码实现(含动态演示) 

三、KMP算法完整代码


一、KMP算法简介

        KMP算法是一种改进的字符串匹配算法,由 D.E.Knuth ,J.H.Morris 和 V.R.Pratt 提出的,因此人们称它为克努特 — 莫里斯 — 普拉特操作(简称 KMP 算法)。 KMP 算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个 next() 函数实现, 函数本身包含了模式串的局部匹配信息。 KMP 算
法的时间复杂度 O(m+n) [1]   。 来自 ------- 百度百科。(这里应该是next数组,通过函数获取到next数组)

二、KMP算法的详细图解

1. 先了解BF算法的基本思路 

在了解KMP算法之前,我们先来了理解BF算法的原理。

        BF算法,即暴力(Brute Force)算法,是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和 T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法是一种蛮力算法。

代码如下:

int strStr(string haystack, string needle) {
    int n = haystack.size();
    int m = needle.size();
    for (int i = 0; i <= n - m; i++) {// i 每次回退到前一个位置的下一个位置
        bool flag = 1;
        for (int j = 0; j < m; j++) { // j 每次匹配失败后回退到0号位置
            if (haystack[i + j] != needle[j]) {
                flag = 0;
                break;
            }
        }
        if (flag) {
            return i;
        }
    }
    return -1;
}

其中i < n - m; 的原因,如下图所示:当 i 走到下标5的位置时,剩下的子串(包括其本身)是符合子串的长度的,但是当 i 走到下标6的位置时,就没有足够的数量去匹配子串;

2. 简单了解KMP算法 

接下来我们来看一下KMP算法:

        如下图所示:如果按照BF算法,i 回退到下标1的位置,j 回退到下标0的位置,但是它们的字符并不相等,而且之前都匹配成功了5个字符,这样的操作显得有些多余;
        KMP算法和BF算法的本质区别是:主串的 i 并不会回退,并且子串 j 也不会回退到 0 号位置;而是回退到一个特定的位置。

那么这个特定的位置到底是哪个位置呢?

        从上个图可以看到,当 j 回退到2号位置时,子串能够最大程度上在冲突位置之前匹配主串。

        但是我们发现,当 j 回退到2号位置时,i 和 j 所对应的字符并不相同,此时我们的 j 还是需要回退的,这个我们到后面讲,但是你想一下,如果 j 回退到2号位置时,刚好 i 和 j 所对应的字符相同,那么我们的回退就是很成功的。(乍一听是这么个到道理,我们再继续往下看)

        刚刚 j 回退到2号位置,我们知道了为什么要回退的原因,那么这个2号位置怎么求出来,更是我们所关心的。如何求呢?(先不要着急,KMP算法还是比较抽象的,先看看图解)

        从图中我们可以发现,5号位置是发生冲突的位置,在5号位置之前,主串的【3】【4】下标和子串【3】【4】下标都是a、b;(这不是废话吗?要是不相同i 和 j 怎么可能走到5呢!!)并且子串的【0】【1】下标也是a、b;它们的长度是不是2啊,j 就回退到2;(可能有人会觉得这是什么逻辑或者说你这不是凑巧吗?)我们就以这种例子看,到后面你就不会这么觉得了;(这里没理解明白的,没关系,直接向下看)

3. next数组的引入

next数组的作用:保存字串某个位置失败后要回退的哪个位置。

        KMP 的精髓就是 next 数组:也就是用 next [ j ] = k;来表示,不同的 j 来对应一个 k值, 这个 k 就是你将来要将 j 移动到哪个位置。而 k 的值是这样求的:

  1. 规则:找到匹配成功部分的两个相等的真子串(不包含本身),一个以下标 0 字符开始,另一个以 j-1 下标字符结尾。
  2. 不管什么数据 next [0] = -1;next [1] = 0;

这里听完,或许你还不知道怎么算next数组,接下来我们做两组练习 

求解next数组:对于子串”ababcabcdabcde”, 求其的 next 数组?

说明一下:

  1. 在图中字符数组下面的下标表示所求的next数组对应的内容,表明发生匹配失败时需要回退到哪个下标(也就是回退到字符数组上方对应的下标)
  2. 在求next数组的时候,你必须保证,在发生不匹配时,之前匹配成功过的字符中,要存在两个相同的真子串,并且这两个真子串是一个从下标0开始,一个从以标 j - 1 结尾;下面这种情况是不可以的

         注:对于上面的next数组为什么从-1开始,第二个是0;有些人喜欢从0开始,有些喜欢从-1开始,如果 j 回退到-1,那么 j++ 就到了子串下标0的位置,如果是 j 回退到0,那么就刚好;至于第二个给0,其实也能够给想到,当你在第二个位置发生不匹配时,前面肯定找不到两个相同的真子串。

        我相信大家对上面的next数组如何求解应该问题不大。这里再提供一个练习,看看自己有没有掌握;

对于子串”abcabcabcabcdabcde”, 求其的 next 数组?

答案:-1,0,0,0,1,2,3,4,5,6,7,8,9,0,1,2,3,0

-------------------------------------------------------------------------------------------------------------------------------- 

        紧接着又有一个问题出现了,我们虽然通过画图可以计算到next数组,但是如何用代码实现出来,是我们最关心的。这里需要用到公式推导,接下来看下图的推导过程。

总结:

  1. 首先假设: next[ i ] = k 成立,那么,就有这个式子成立:
  2. P[0]...P[k-1] = P[x]...P[i-1];得到: P[0]...P[k-1] = P[i-k]..P[i-1];
  3. 到这一步:我们再假设如果 P[k] = P[i];
  4. 我们可以得到 P[0]...P[k] = P[i-k]..P[i];那这个就是 next[i+1] = k+1;

4. next数组的代码实现(含动态演示) 

void getNext(int* next, const string& s) {
    int len = s.size();
    next[0] = -1;
    if (len == 1) return;
    next[1] = 0;
    int k = 0;//前一项的K
    int i = 2;//此时i表示当前下标,也就是下一项
    while (i < len) {
        if (k == -1 || s[i - 1] == s[k]) 
        {
            next[i] = k + 1;
            k++;
            i++; 
        }
        else {
            k = next[k];
        }
    }
}

next数组动态演示: 

 

三、KMP算法完整代码

class Solution {
public:
    void GetNext(int* next, const string& s) {
        int len = s.size();
        next[0] = -1; //next数组第一个元素直接给-1
        if (len == 1)return;//如果传过来的子串只有一个字符,我们直接返回,因为next数组已经给定了
        next[1] = 0;  //next数组第二个元素直接给0
        int k = 0; //前一项的K
        int i = 2; //此时i表示当前下标,也就是下一项
        while (i < len) {
            if (k == -1 || s[i - 1] == s[k]) 
            {
                next[i] = k + 1;
                k++;
                i++; 
            }
            else {
                k = next[k];
            }
        }
    }

    int strStr(string haystack, string needle) {
        int len1=haystack.size(); //计算出主串的长度
        int len2=needle.size();   //计算出子串的长度

        if(len1 == 0 && len2 != 0) return -1; 
        if(len1 == 0 && len2 == 0) return 0;  
        if(len1 != 0 && len2 == 0) return 0;
  
        int i = 0;//遍历主串
        int j = 0;//遍历字串

        int *next = new int[len2];//开辟一个next数组,空间大小是子串的大小
        GetNext(next, needle); //获取子串的next数组

        while(i < len1 && j < len2)
        {
            //如果主串和子串的字符相同或者j回退到了-1位置,我们都要把i和j进行++
            if((j == -1) || haystack[i] == needle[j])
            {
                i++;
                j++;
            }
            else    
            {
                j = next[j];//主串和子串的字符不匹配,j需要依靠next数组进行回退操作
            }
        }
        
        if(j >= len2) return i - j;//子串遍历完了
        else return -1;//子串没有遍历完了
    }
};

        最后,对于KMP算法,确实是比较抽象的,大家在看到这篇博客的时候,如果发现有什么错误或者有什么不懂的地方,直接私信博主,相互交流。共同进步

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/160775.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【算法】二分图判定

目录1.概述2.代码实现3.应用本文参考&#xff1a; LABULADONG 的算法网站 1.概述 &#xff08;1&#xff09;二分图 (Bipartite Graph)&#xff0c;又称为二部图&#xff0c;是图论中的一种特殊模型。 设 G (V, E) 是一个无向图&#xff0c;如果顶点 V 可分割为两个互不相交的…

Unity 和vs2022对接问题

第一个问题&#xff1a;在vs中编写好的程序在unity中预览出现乱码&#xff1b;提示&#xff1a;只要是乱码的问题90%离不开编码表Unity中的编码表是utf-8,而vs中默认的应该是GB2312。英文还好&#xff0c;中文可定就会出现乱码&#xff0c;解决方法也很简单&#xff1a;把vs中的…

【Python基础四】入门级朋友看的超详教程

前言 这是最后一篇基础的文章啦 往期文章&#xff1a; 【Python基础一】入门级朋友看的超详教程 【Python基础二】入门级朋友看的超详教程 【Python基础三】入门级朋友看的超详教程 刚开始接触Python的宝子&#xff0c;有什么不懂的都可以私信我哦 我还准备了大量的免费…

目标检测:YOLO V2思路解读

目标检测&#xff1a;YOLO V2思路解读YOLO V1存在的问题主要改进Batch NormalizationHigh Resolution ClassifierConvolutional With Anchor BoxesDimension ClusterDirect location PredictionFine-Grained FeaturesMulti-Scale TrainingLoss FunctionYOLO V1存在的问题 对于…

使用Redis代替Session实现短信登陆

1.集群的Session共享问题 多台Tomcat并不共享Session存储空间&#xff0c;当请求切换到不同tomcat服务器时会导致数据丢失&#xff1a; 当用户量增多&#xff0c;我们需要进行负载均衡、对tomcat做水平扩展&#xff0c;可是存储在Tomcat里的Session不是共享的&#xff0c;这…

从C和C++内存管理来谈谈JVM的垃圾回收算法设计-上

从C和C内存管理来谈谈JVM的垃圾回收算法设计-上引言C内存模型malloc堆内存分配过程malloc为什么结合使用brk和mmapmalloc如何通过内存池管理Heap区域垃圾收集器引言 本文想和大家来探讨一下JVM是如何对堆内存进行管理和垃圾回收,相关书籍如深入理解JVM第三版中已经介绍过了相关…

OSCP-Vulnhub靶机记录-digitalworldlocal-fall

Vulnhub靶机记录-digitalworldlocal-fall靶机描述安装扫描枚举使用kali自带的FUZZ权限提升靶机描述 靶机地址&#xff1a;https://www.vulnhub.com/entry/digitalworldlocal-fall,726/ Description To celebrate the fifth year that the author has survived his infosec ca…

也来聊聊滑块验证码的那些事

单位做攻防演习&#xff0c;我扮演攻击方尝试破解。发现滑块验证码做了升级&#xff0c;比之前复杂了很多。好在仍然是一维验证&#xff0c;不用太麻烦。https接口里读出的是json对象&#xff0c;先从对象里取出图片转的base64编码&#xff0c;然后把字符串转回成numpy.ndarray…

Verilog HDL 基础语法

一、逻辑值 0: 逻辑低电平&#xff0c;条件为假 1: 逻辑高电平&#xff0c;条件为真 z: 高阻态&#xff0c;无驱动 x: 未知逻辑电平二、实际例子 1. 模块名一般与文件名相同 线网型变量会被映射成一条真实存在的物理连线。 寄存器型变量会被映射成一个寄存器。 2. 参数 para…

2、JavaScript快速入门

2.1 引入JavaScript 内部标签 <!-- 在script标签内写JavaScript(简称js)代码&#xff0c;代码块可以放在head中&#xff0c;也可以放在body中--> <script>// alert:弹窗alert(Hello,world!); //注意以分号结尾 </script>外部引入 hello.js alert(Hello,worl…

分享120个ASP源码,总有一款适合您

ASP源码 分享120个ASP源码&#xff0c;总有一款适合您 链接&#xff1a;https://pan.baidu.com/s/1WwTsUTLS_qLvP-TC1w-1vQ?pwdvxpk 提取码&#xff1a;vxpk 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c;大家下载…

OB0207 obsidian 自动获取url链接:auto-link-title插件使用

序号解读&#xff1a; 01——软件基础使用、基础语法 02——插件使用 03——综合实战 0 写在前面 Ob社区插件汇总&#xff1a;Airtable - OB社区插件汇总 - Johnny整理 - 每周更新 - B站 Johnny学Explore the "OB社区插件汇总 - Johnny整理 - 每周更新 - B站 Johnny学&qu…

过去一年渲染了3亿帧,助力了63.81亿票房、1150亿播放量丨瑞云渲染年度大事记

2022年&#xff0c;注定是充满未知和挑战的一年。抗疫三年&#xff0c;终于在2022年底迎来放开&#xff0c;我们怀着忐忑的心情告别了核酸、行程码和封控&#xff0c;成为了自己健康的第一负责人。这段时间大家应该都忙着和病毒做斗争吧&#xff0c;瑞云各个岗位的小伙伴们也都…

6.7、万维网(如HTTP超文本传输协议)

1、基本介绍 万维网 WWW (World Wide Web&#xff09;并非某种特殊的计算机网络\color{red}并非某种特殊的计算机网络并非某种特殊的计算机网络。 它是一个大规模的、联机式的信息储藏所&#xff0c;是运行在因特网上的一个分布式应用。 万维网利用网页之间的超链接\color{r…

Web进阶:Day5 移动适配、rem、less

Web进阶&#xff1a;Day5 Date: January 10, 2023 Summary: 移动适配、rem、less 移动适配 移动适配指网页元素的宽高都要随着设备宽度等比缩放 rem &#xff1a; 目前多数企业在用的解决方案 vw / vh&#xff1a;未来的解决方案 rem 目标&#xff1a;能够使用rem单位设置网…

2022年跨境物流指数研究报告

第一章 行业概况 指提供跨境物流服务的行业。跨境物流是指在电子商务环境下&#xff0c;依靠互联网、大数据、信息化与计算机等先进技术&#xff0c;物品从跨境电商企业流向跨境消费者的跨越不同国家或地区的物流活动。 图 物流运输行业产业链结构图 资料来源&#xff1a;资产…

Tapdata Cloud 场景通关系列:集成阿里云计算巢,实现一键云上部署真正开箱即用

【前言】作为中国的 “Fivetran/Airbyte”, Tapdata Cloud 自去年发布云版公测以来&#xff0c;吸引了近万名用户的注册使用。应社区用户上生产系统的要求&#xff0c;Tapdata Cloud 3.0 将正式推出商业版服务&#xff0c;提供对生产系统的 SLA 支撑。Tapdata 目前专注在实时数…

VS2010 安装NuGet NPIO 基础连接已经关闭:发送时发生错误

1.下载Nuget并安装 NuGet Package Manager - Visual Studio Marketplace 工具->扩展管理器可看见 2.安装NPOI 3. 如果遇见基础连接已经关闭:发送时发生错误 要把https://packages.nuget.org/改为https://www.nuget.org/api/v2/ VS2019要使用https://www.nuget.org/api/v…

OAuth2.0 详解

OAuth2.0介绍 OAuth&#xff08;Open Authorization&#xff09;是一个关于授权&#xff08;authorization&#xff09;的开放网络标准&#xff0c;允许用户授权第三方 应用访问他们存储在另外的服务提供者上的信息&#xff0c;而不需要将用户名和密码提供给第三方移动应用或分…

【日常系列】LeetCode《26·动态规划1》

数据规模->时间复杂度 <10^4 &#x1f62e;(n^2) <10^7:o(nlogn) <10^8:o(n) 10^8<:o(logn),o(1) lc 509【剑指 10-1】&#xff1a;斐波那契数列问题 - 动态规划入门 https://leetcode.cn/problems/fibonacci-number/ 提示&#xff1a; 0 < n < 30 #方案…