C++ 实现字符串查找 KMP算法

news2024/11/17 15:46:39

前言

  • 众所周知,字符串查找的应用范围非常广,网页上有各式各样的浏览器搜索,再到编程需要的vsCode或vsStudio都自带了搜索功能;一个查找算法的优劣可以直接影响用户的搜索体验如何
  • 但鉴于暴力搜索算法的O(m * n)复杂度,就有了K姓大佬,M姓大佬,P姓大佬设计出来的一套O(n + m)字符串查找算法;于是他们的名字被载入史册,且这套算法被命名为KMP算法
  • leetcode指路: 找出字符串中第一个匹配项的下标

刚接触KMP算法时,你大概会觉得这个算法非常诡异,一波诡异的操作处理后生成了一个next数组,又一波诡异的遍历操作后,就找到了目标位置(???WTF);
代码倒是不长,每个单词都认识,但是放一块就不认识了,像极了四级英语阅读。。

一、需要提前明白的概念

1. 最大前后缀

首先你需要知道最大前后缀是什么;

  • 最大前后缀就是 在一段字符串中,从首尾分别开始,可能形成的最大相等字符子串

以下图为例:在箭头位置上,它的前置最大前后缀为abb
图1.1

以下图为例:在箭头位置上,它的前置最大前后缀为abbsab
图1.2

看到这里你可能会有疑惑,最大前后缀的规则是什么?下面简单进行一个总结:

  1. 最大前缀 与 最大后缀必须相等
  2. 最大前后缀必须小于它可取的长度;即:图一中最大前后缀不能同时取abbsabb(s以前的所有字符),图二中最大前后缀不能取b以前的所有字符,那样的话就失去了它的意义。
  3. 最大前后缀必须尽可能的长;图二中可能相等的前后缀有:ababbsabb,我们需要取其中较大的那个。

2. next数组

  • next数组专门用于保存字符串的每个位置之前,它的最大前后缀长度
    比如在刚刚的那个字符串所对应的next数组
    图1.3

  • 再举一个例子:
    图1.4

  • 它的每个位置,都记录着在它位置之前的子串中的最大前后缀长度

至于next数组是哪里来的,有什么用?会放在后面讲。

二、主逻辑

1.主要步骤:

这里先展示一下如何使用最大前后缀

  • 比如有字符串str1str2,要求在str1中查找str2位置;(这里的字符串跟上面关于next讲解的部分的没关系,千万不要搞混了)

  • 首先假设我们已经得到了str2的正确的next数组
    那么我们怎么通过已知的信息来对暴力解法进行优化呢?

  • 经过之前7个字符的比较,指针顺利来到了s位置
    图2.1

  • 此时发现该位置两个该位置不匹配,那咋办?

  • 此时我们已经知道了str2next数组中,x位置上的前置子串最大前后缀长度为3

  • 那这是不是代表,我把str2字符串向前移到相对应得位置,那么此时str1str2得前缀依然是匹配上了:
    图2.2

这一步,过滤了暴力解法的主串指针回溯过程
那么为什么可以这么跳过呢?
主要分两个证明:

  1. 前后缀相等,则字符串跳转后,下标位置处 前字串也一定匹配
  2. 跳转经过的区间内,一定不存在与主串匹配的存在

问题一自不用说,肉眼可见,下面主要证明步骤2;为什么经过区间一定不含可能匹配的存在

  • 假设:如果在经过区间上存在可匹配的存在,那么将说明此时该区间内存在更大的前后缀,这与next数组中的已求得数据不符,所以该结论不成立。
    图2.3

2.跳跃方式

  • 跳跃到哪个位置将由next数组决定,next数组与str2共用一个下标,需要查询前缀位置直接在next的对应下标位置寻找

  • 例如下图中,在箭头位置 首次匹配失败,通过直接查询next数组中 x的对应位置找到需要跳转的下标。
    图2.4

  • 这里还匹配失败如下图:
    图2.5

  • 那么这里就再次进行跳转,跳转到0位置继续匹配
    图2.6

  • 如果str2指针已经跳到0位置,还匹配失败的话,就挪动主串指针:
    图2.7

以上就是主串的整个匹配过程。
计算可得:这个过程的最大时间复杂度不超过O(2 * N)

3.主要代码

int strStr(string str1, string str2) {
        vector<int> next;
        func(next, str2);					//求next数组

        int idx1 = 0;
        int idx2 = 0;
        while(idx1 < str1.size())
        {
            if(str1[idx1] == str2[idx2])	//匹配上就两个指针一起往下走
            {
                idx1++;
                idx2++;
                if(idx2 == str2.size())
                {
                    return idx1 - idx2;
                }
            }
            else if(idx2 == 0)				//没匹配上而且idx2在0位置
            {
                idx1++;
            }
            else{							//没匹配上  idx2不在0位置
                idx2 = next[idx2];
            }
        }
        return -1;
    }

三、求next数组

  • next数组所用的是经典的动态规划过程,简单来说就是两个指针一个在头部找前缀,一个指针在尾部找后缀。
  • 这里不能使用暴力求解的方法,因为暴力求解的最高时间复杂度为O(m ** 2),那么在某些极端情况下,整个算法的时间复杂度会涨到O(n ** 2)

1.总过程

我不论如何,先给next数组填一个0
图3.1

  • 为啥这么做呢?是因为下标0位置不包含前缀,而且这一位也不会被访问到,我们真正开始计算next数组是从1号下标开始。

  • 创建两个指针,分别寻找前缀和后缀,这里用颜色区分。
    图3.2

  • 第一步发现:两个字符相等,那么都自增,后缀位置next数组内填(前缀指针下标 + 1)(0 + 1)
    图3.3

  • 第二步发现:还相等,重复上过程
    图3.4

  • 第三步发现:她两不相等,那么就通过已知最大前缀数来找到需要跳转到的位置:next[1] == 1
    图3.5

  • 第四步发现:她两还不相等,那么通过已知最大前缀数来找到需要跳转到的位置:next[0] == 0
    图3.6

  • 此时发现她两还不相等,但是 前缀下标已经走到了0位置, 所以说明s位置不存在任何可以匹配的相等前后缀,就给该位置赋 0 然后后缀指针自增

图3.7
继续重复上述过程:
图3.8
图3.9
图3.10
以上就是整个next数组的求解过程,主要采用动态规划的形式,以类似主逻辑的方式将整个next数组求解。

四、代码

class Solution {
    //求next 数组
    void func(vector<int>& next, string& str2)
    {
        next.resize(0);
        next.push_back(0);
        int idx1 = 1;
        int idx2 = 0;
        while(idx1 < str2.size())
        {
            //相等则next填充,两指针自增
            if(str2[idx1] == str2[idx2])
            {
                next.push_back(++idx2);
                idx1++;
            }//格式串在最左部,
            else if(idx2 == 0)
            {
                next.push_back(0);
                idx1++;
            }
            else{//可以向前找
                idx2 = next[idx2 - 1];
            }
        }
    }
public:
	int strStr(string str1, string str2) {
        vector<int> next;
        func(next, str2);					//求next数组

        int idx1 = 0;
        int idx2 = 0;
        while(idx1 < str1.size())
        {
            if(str1[idx1] == str2[idx2])	//匹配上就两个指针一起往下走
            {
                idx1++;
                idx2++;
                if(idx2 == str2.size())
                {
                    return idx1 - idx2;
                }
            }
            else if(idx2 == 0)				//没匹配上而且idx2在0位置
            {
                idx1++;
            }
            else{							//没匹配上  idx2不在0位置
                idx2 = next[idx2];
            }
        }
        return -1;
    }
}

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

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

相关文章

精选测试面试题

一、Web自动化测试 1.Selenium中hidden或者是display &#xff1d; none的元素是否可以定位到&#xff1f; 不能,可以写JavaScript将标签中的hidden先改为0&#xff0c;再定位元素 2.Selenium中如何保证操作元素的成功率&#xff1f;也就是说如何保证我点击的元素一定是可以…

图文并茂的介绍用word生成一个很智能的目录

文章目录前期准备设置各级目录的字体样式准备一个新的页面&#xff08;装目录&#xff09;大工告成&#xff01;作为一名程序员&#xff0c;写代码自然是我们的强项&#xff0c;除了写写代码使用计算机的能力也渐渐成为了考察我们的指标&#xff0c;今天我们介绍一个办公小技巧…

【图神经网络】Pytorch图神经网络库——PyG异构图学习

PyG异构图学习举个例子创建异构图Utility函数异构图Transformations创建异构图神经网络自动转换GNN模型使用异构卷积包装器部署现有的异构算子异构图采样参考资料大量真实世界数据集存储为异构图&#xff0c;这促使Pytorch Geometric&#xff08;PyG&#xff09;中引入专门的功…

Java项目:Springboot实现的一个简单博客管理系统

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 本项目为前后台管理系统&#xff0c;包括博主与游客两种角色&#xff1b; 博主角色包含以下功能&#xff1a; 博主登录,发博客,博主可以删除博客…

使用yolov5 v7.0进行实例分割

1. 介绍 U版yolov5最新推出了v7.0版本,新增了基于yolov5进行实例分割的代码。作者提到yolov5 v7.0实现的实例分割是超越了所有的SOTA模型的效果,是目前为止速度和精度最高的。 2. 代码的使用 2.1 Setup 克隆GitHub仓库,安装依赖项,检查PyTorch和GPU。 git clone http…

GEO芯片数据分析更新(补富集分析与WGCNA)

GEO数据挖掘&#xff0c;表达芯片分析 举例&#xff1a;王同学近期拟通过生物信息学相关软件与数据库来探讨女性非抽烟者的非小细胞肺癌预后相关的显著性基因及潜在的治疗靶点&#xff0c;他在NCBI上查询到了1套芯片数据GSE19804。请帮助他完成该项目的设计与分析。 上一篇博…

Linux系统基础——内核初始化

内核初始化 特此说明: 刘超的趣谈linux操作系统是比较重要的参考资料&#xff0c;本文大部分内容和所有图片来源于这个专栏。 1 背景知识 BootLoader阶段后&#xff0c;cpu从实模式转换成保护模式。有了更强的寻址能力后&#xff0c;内核也已经加载到内存了&#xff0c;系统内…

2. 做一个极简 UI 库之Toast 组件

效果 API 设计 先设计好了 API 写起来代码才不会犯迷糊 Toast(message: string; otherParams?: ToastParams): ToastReturninterface ToastParams {time?: number;appendTo?: string | HTMLElement;dangerouslyUseHTMLString?: boolean; }interface ToastReturn {close():v…

Node.js - Express

文章目录目标一、初识 Express1、Express 简介&#xff08;1&#xff09;什么是 Express&#xff08;2&#xff09;进一步理解 Express&#xff08;3&#xff09;Express 能做什么2、Express 的基本使用&#xff08;1&#xff09;安装&#xff08;2&#xff09;创建基本的 Web …

认识 Fuchsia OS

认识 Fuchsia OS 1 说明背景 1.1 基本信息 开发者: Google编程语言: C、C、Rust、Go、Python、Dart内核: Zircon运作状态: 当前源码模式: 开放源代码初始版本: 2016年8月15日支持的语言: 英语支持平台: ARM64、X86-64内核类别: 微内核 基于能力 实时操作系统许可证: BSD 3 c…

node.js+uni计算机毕设项目高校迎新管理小程序(程序+小程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等…

【2023 AR元宇宙过圣诞!】《Merry Meta Christmas》

啥也不说了&#xff0c;先看最终效果 3D场景资源、EasyAR_Plugin、图片与安卓App资源均已上传&#xff0c;点击该处下载 一、前言 圣诞节的真正含义是为了纪念耶稣诞生&#xff0c;象征着团圆美满&#xff0c;万物复苏&#xff0c;日子变得愈发美好 2021年是元宇宙的元年&…

UE5 狐獴演示Demo分析

1.特效的生成方式 1.1临时特效的生成&#xff1a;使用了已生成轨道临时创建该特效&#xff08;不用在场景中放入该特效&#xff0c;而是临时创建即可&#xff09;、系统生命周期轨道设置该特效的播放时长 1.2长期特效的生成&#xff1a;特效时长为该镜头片段长度 2.特效的类…

输出数组中每一行(列)中的最小值(最大值)numpy.amin()numpy.amax()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 输出数组中每一行&#xff08;列&#xff09;中的最小值&#xff08;最大值&#xff09; numpy.amin() numpy.amax() [太阳]选择题 对下面代码中np.amin(myList, 0)输出的结果为&#xff1f;…

java基于ssh的旅游系统

本项目主要发西安各个旅游景点和附近酒店信息的网站&#xff0c;用户可以根据旅游团一起旅游&#xff0c;可以也可以自驾游&#xff0c;还可以发布旅游活动等。 演示视频 https://www.bilibili.com/video/BV1wv411x7cg/?share_sourcecopy_web&vd_sourceed0f04fbb713154db…

【Vue】七、Vue-cli工程化开发

后端程序员的vue学习之路一、 Vue-cli安装Vue-cli1、安装node.js2、配置node.js环境变量3、 Npm仓库设置淘宝源4、全局安装 vue-cli5、创建vue应用程序1、 创建vue项目基础骨架&#xff1a;2、 运行项目&#xff1a;6、vue项目结构二、Vue.js项目运行逻辑分析1、 npm run dev命…

3.11.2、虚拟局域网 VLAN 实现机制

虚拟局域网 VLAN 技术是在交换机上实现的&#xff0c;需要交换机能够实现以下两大功能 能够处理带有 VLAN 标记的帧&#xff1a;IEEE 802.1Q 帧交换机的各端口支持不同的端口类型&#xff08;帧的处理方式有所不同&#xff09; 1、IEEE 802.1Q 帧 IEEE 802.1Q 帧&#xff08…

Java项目:SpringBoot美容院预约管理系统

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 本系统分为管理员与普通用户两种角色&#xff1b; 管理员角色包含以下功能&#xff1a; 登录,首页,新增管理员,管理员信息列表,网站用户信息列表…

node.js+uni计算机毕设项目基于微信小程序校园心理咨询(程序+小程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等…

RabbitMQ 第一天 基础 1 MQ的基本概念 1.1 MQ 概述 1.2 MQ的优势和 劣势 1.3 MQ的优势

RabbitMQ 【黑马程序员RabbitMQ全套教程&#xff0c;rabbitmq消息中间件到实战】 文章目录RabbitMQ第一天 基础1 MQ的基本概念1.1 MQ 概述1.1.1 MQ 概述1.1.2 小结1.2 MQ的优势和 劣势1.2.1 概述1.3 MQ的优势1.3.1 应用解耦1.3.2 异步提速1.3.3 削峰填谷1.3.4 小结第一天 基础…