KMP字符串匹配算法详解

news2024/12/21 23:39:18

目录

    • 简单的暴力匹配算法
    • KMP算法
      • next数组
      • next数组的优化

简单的暴力匹配算法

对于字符串的匹配通常是给出一个主串str和一个模式串sub,然后在主串pos位置开始匹配,如果能在str中找到sub那么就返回sub在str中首次出现的首个字符的下标,否则返回-1。我们能想到的最直接的方法就是用三个指针p,i,j 这里我们就以pos=0 为例子讲解,先让p=0,i=p,j=0,开始匹配
如果str[i]==sub[j] 那么++i,++j 如果不相等那么j回到初始位置即j=0,然后p++ ,i=p,也就是i又从上一轮往后一个位置开始匹配,最后如果j能走到末尾就匹配成功返回 i-j; 否则返回-1,这种暴力算法时间复杂度是O(n^2)。

 int strStr(string str, string sub,int pos) {
        int i=pos;
        int j=0;
        int m=str.size(),n=sub.size();
        if(m==0||n==0) return -1;
        while(i<m&&j<n)
        {
            if(str[i]==sub[j])
            {
               
                ++i;
                ++j;
            }
            else
            {
                ++pos;
                i=pos;
                j=0;
            }
        }
        if(j>=n)
         return i-j;
        return -1;
    }

KMP算法

KMP算法是一种改进的字符串匹配算法,其核心就是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next函数实现,函数本身包含了模式串的局部匹配信息,KMP算法的时间复杂度O(m+n)。
KMP和BF唯一不一样的地方在,主串的i不管匹配成功与否都不会回退,j也不一定每次都回退到0位置,j该回退的位置我们都记录在next数组中。

在这里插入图片描述

比如上图,i和j从0位置开始匹配,在5位置匹配失败,这是j可以只要回退到2位置继续和i匹配
可以知道,i能走到5位置自然是前面有一部分匹配上了,这里 str 3 4 位置就跟sub 0 1位置匹配上l
所以i不回退,j从2位置继续匹配。我们把每次匹配失败对于该回退到那个位置记录在next[j]中

next数组

KMP的精髓就是next数组用next[j]=K 来表示,sub在j位置匹配失败,应该回退到k位置取,求next数组的规则是
1.找到匹配成功部分的两个相等的真子串,不能是同一个,一个以下标0字符开始,另一个以j-1下标字符结尾。
2.就是初始化next数组,不管什么数据next[0]=-1,next[1]=0,

next[j]=k就是说找到了两个相等的真子串 sub[0]…sub[k-1] 和sub[x]…sub[j-1] 长度就是k,找不到就是0

比如求”ababcabcdabcde”的next数组

在这里插入图片描述
求对”abcabcabcabcdabcde”
在这里插入图片描述
所以通过目测可以算出next[j],接下来就要思考,如果已经知道了next[i]的值,如果求next[i+1]的值
通过上面连个例子可以看出,相邻位置的k值增加大方式只能是+1,不会跳跃式增加,实际上,减少的方式不一定是直接到0,如果next[i]=k 那就是sub[0]…sub[k-1] 和sub[x]…sub[i-1]相等,现在要计算next[i+1] 应该判断sub[k]==next[i] 是否为真,如果为真就有sub[0]…sub[k-1] sub[k] 和
sub[x]…sub[i-1] sub[i] 是连个相等的真字串了,长度就是k+1 也就是next[i+1]=k+1,如果不相等k
就要回退到next[k] 即 k=next[k] 所以我们就可以通过sub 计算出next数组了

void getnext(const string&sub,vector<int>&next)
   {
        int n=next.size();
       next[0]=-1;
       if(n>1)     // 防止越界  ,后面有 i<n判断不会越界
       next[1]=0;
       int k=0;    //完成了 0  1 位置的初始化,就开始计算下面next位置的值了,这里k就是1位置的值
       int i=2;
      
       while(i<n)
       {
           if(k==-1||sub[i-1]==needle[k])  //如果回退到了-1 那么i位置就应该退到0 所以也进入然后设置为0
           {
               next[i]=k+1;
               ++k;   //设置完i位置 后面i+1,的值我们要用i位置的k值 所以也是k++
               ++i;
           }
           else
           {
               k=next[k];   //回退
           }
       }
   }
 int KMP(const string&str,const string&sub,int pos)
   {
       int i=pos;
       int j=0;
       int m=str.size();
       int n=sub.size();
       if(m==0||n==0) return -1;   //前期的判断
      
        vector<int>next(n),nextval(n);
        getnext(sub, next);  // 有优化后的next数组,即nextval数组,用其中任意一个都行
        getnextval(sub, next,nextval);
       while(i<m&&j<n)
       {
           if(j==-1||str[i]==sub[j])  //同样如果j在初始时就匹配失败了,跳到-1了同样可以进入让i++ ,j从0开始匹配
           {
               ++i;
               ++j;
           }
           else
           {
              // j=next[j];
              j=nextval[j];
           }
       }
       if(j>=n)
        return i-j;
      return -1;
   }

next数组的优化

我们在一个位置匹配失败j是回退的,因为str[i]!=sub[j] 回退有j=next[j] 如果回退位置的字符和没回退位置的字符时相同的那么还是会匹配失败,继续回退,所有我们优化,然后回退一步到位
比如 sub aaaaaaaab

在这里插入图片描述
nextval数组的求法就是,首先nextval[0]自然是-1 后面i>0, 如果当前要回退到的位置的字符正好等于当前字符,那么i处的nextval值就是回退位置的nextval值,否则就是自己的next值

   void getnextval(const string&sub,const vector<int>&next,vector<int>&nextval)
   {
       nextval[0]=-1;
        int n=nextval.size();
       
      
       for(int i=1;i<n;++i)
       {
           if(sub[i]==sub[next[i]])
             nextval[i]=nextval[next[i]];
           else
             nextval[i]=next[i];
       }
   }

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

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

相关文章

地球系统模式(CESM)

目前通用地球系统模式&#xff08;Community Earth System Model&#xff0c;CESM&#xff09;在研究地球的过去、现在和未来的气候状况中具有越来越普遍的应用。CESM由美国NCAR于2010年07月推出以来&#xff0c;一直受到气候学界的密切关注。近年升级的CESM2.0在大气、陆地、海…

【论文笔记】VideoGPT: Video Generation using VQ-VAE and Transformers

论文标题&#xff1a;VideoGPT: Video Generation using VQ-VAE and Transformers 论文代码&#xff1a;https://wilson1yan. github.io/videogpt/index.html. 论文链接&#xff1a;https://arxiv.org/abs/2104.10157 发表时间&#xff1a; 2021年9月 Abstract 作者提出了…

git 常用命令及遇到问题

自己没事&#xff0c;把git常用命令做个记录总结。方便自己和初学者查看&#xff0c;本文针对初学者&#xff0c;如果你已经是工作多年高手&#xff0c;请跳过。 git的几个区认识&#xff0c;分别为工作区&#xff0c;缓存区&#xff0c;版本库。 工作区&#xff1a;包含.git…

靶机精讲:BNE0x03Simple

主机发现 nmap扫描 端口扫描 服务扫描 脚本扫描 第二十页路径有目录 web渗透 搜索该版本漏洞 CuteCMS漏洞利用 下载下来 查看文件&#xff0c;发现是远程文件上传漏洞 按步骤进行注册 点进去 构造利用文件&#xff0c;反弹shell 最后语法错误应为0>1 尝试上传 根据漏洞扫…

实验04:图像压缩(DP算法)

1.实验目的&#xff1a; 掌握动态规划算法的基本思想以及用它解决问题的一般技巧。运用所熟悉的编程工具&#xff0c;运用动态规划的思想来求解图像压缩问题。 2.实验内容&#xff1a; 给定一幅图像&#xff0c;求解最佳压缩&#xff0c;使得压缩后的文件最小。 3.实验要求…

容易忽视的细节:Log4j 配置导致的零点接口严重超时

作者&#xff1a;vivo 互联网服务器团队- Jiang Ye 本文详细的记录了一次0点接口严重超时的问题排查经历。本文以作者自身视角极具代入感的描绘了从问题定位到具体的问题排查过程&#xff0c;并通过根因分析并最终解决问题。整个过程需要清晰的问题排查思路和丰富的问题处理经验…

大话数据结构-查找

1 查找概论 查找表&#xff08;Search Table&#xff09;是由同一类型的数据元素&#xff08;或记录&#xff09;构成的集合&#xff0c;如下所示&#xff1a;   关键字&#xff08;Key&#xff09;是数据元素中某个数据项的值&#xff0c;又称为键值&#xff0c;用它可以标…

LiteFlow规则引擎的入门

文章目录 1、LiteFlow简介2、解决的痛点3、快速开始3.1 引入依赖3.2 配置规则文件的位置3.3 定义组件3.4 指定规则3.5 编写客户端3.6 运行以及说明3.7 其他的组件 4、对于快速开始的思考5、LiteFlow的脚本组件5.1 脚本的定义5.2 脚本的使用5.3 关于脚本使用的思考 6、规则引擎的…

开源Qt Ribbon控件——SARibbon的布局思路及介绍

开源Qt Ribbon控件——SARibbon的布局思路及介绍 SARibbon的布局SARibbon名词定义Office布局模式——SARibbonBar::OfficeStyleWPS布局模式——SARibbonBar::WpsLiteStylepannel的布局行数3行模式2行模式 原文链接&#xff1a;https://blog.csdn.net/czyt1988/article/details/…

scala之基础面向对象

scala 既是面向对象 也是函数式编程 从Java 发展而来&#xff0c;依赖JVM环境 一、 scala 在linux中运行 scala 模式中直接编写运行 scala文件&#xff0c;load执行 scala编译程序 编译 运行 scala java 二、scala 数据类型 基础数据类型 val 不可变变量 函数式编程 …

Excel使用频率超高的20个函数,90%你没用过

上班必学必会的Excel函数&#xff0c;不仅是使用频率最大的&#xff0c;还是告别加班的利器。你会的函数越多&#xff0c;解决问题的思路越广&#xff0c;不再束手束脚。态度决定高度&#xff0c;细节决定成败。要想比别人更优秀&#xff0c;只有在每一件小事上比功夫。 组合、…

json-c交叉编译及库移植

编译后的文件 json-c交叉编译及库移植资源-CSDN文库 json-c开源库是一个用c实现的解析json字段的库&#xff0c;嵌入式领域比较实用的库。 由于应用程序需要&#xff0c;需要找移植这个json-c库&#xff0c;所以这里对该库的移植做个简单说明 json-c开源库是一个用c实现的解…

python基于机器学习模型开发实践kaggle旧金山犯罪案件分类预测模型

旧金山犯罪案件分类本质是一个文本的多分类任务&#xff0c;kaggle官网地址在这里&#xff0c;如下所示&#xff1a; 本文主要是以kaggle比赛数据集为基准&#xff0c;开发实践文本多分类任务。 比赛背景 从 1934 年到 1963 年&#xff0c;旧金山因高犯罪率而臭名昭著。时至今…

opengl绘制三角形

1.绘制两个三角形 GLfloat vertices1[] { 0.5f, 0.5f, 0.0f, 0.5f, -0.5f, 0.0f, -0.5f, 0.5f, 0.0f } GLfloat vertices2[] { 0.5f, -0.5f, 0.0f, -0.5f, 0.5f, 0.0f&#xff0c; -0.5f, -0.5f, 0.0f } 也可以用索引的方式&#xff1a; GLfloat vertices[] { 0.5f, 0.5f, 0…

并发编程常见问题复盘

并发编程常见问题复盘 大家好&#xff0c;我是易安&#xff01; 并发编程在计算机科学领域占有举足轻重的地位&#xff0c;它使得程序能够在多个处理器核心上同时执行&#xff0c;从而显著提升程序的性能。然而&#xff0c;并发编程也伴随着许多挑战和问题。这些年来&#xff0…

eacharjs饼状图带百分比

var myChart1 echarts.init(document.getElementById(main1)); myChart1.setOption({title:{text:近30天异常停机的类型TOP5,x:center,y:10px,// textStyle:{// fontSize:12// }},tooltip: {trigger: item//提示 鼠标移动上去},// legend: { // 上面的提示// top: 25%…

端口映射工具PortTunnel

PortTunnel应该是目前最好的端口转发器、端口映射工具(它解决了内外网访问的问题) 可以在我的资源中下载&#xff1a;https://download.csdn.net/download/qq_39569480/87717704 使用该工具前应该保证双方机器网络互通 下面我们模拟一下环境 比如现在有三台机器 A&#xff1a…

Mac环境SpringBoot项目Docker部署(独家完整版)

一、Docker 简介 Docker 是一种开源的容器化平台&#xff0c;允许开发人员将应用程序和所有其依赖项打包成轻量级、可移植的容器&#xff0c;以便在任何地方运行。Docker 的优势和劣势分析如下&#xff1a; 优势: 轻量级:Docker 容器仅包含应用程序及其依赖项&#xff0c;因…

家庭智能吸顶灯一Homekit智能

买灯要看什么因素 好灯具的灯光可以说是家居的“魔术师”&#xff0c;除了实用的照明功能外&#xff0c;对细节的把控也非常到位。那么该如何选到一款各方面合适的灯呢&#xff1f; 照度 可以简单理解为清晰度&#xff0c;复杂点套公式来说照度光通量&#xff08;亮度&#x…

【社区图书馆】二、LED子系统——硬件驱动层

个人主页&#xff1a;董哥聊技术 我是董哥&#xff0c;嵌入式领域新星创作者 创作理念&#xff1a;专注分享高质量嵌入式文章&#xff0c;让大家读有所得&#xff01; 文章目录 1、gpio_led_probe分析1.1 相关数据结构1.1.1 gpio_led_platform_data1.1.2 gpio_leds_priv 1.2 实…