当前位置: 首页 > news >正文

算法刷题笔记 KMP字符串(C++实现,并给出了求next数组的独家简单理解方式)

文章目录

    • 题目描述
    • 基本思路
    • 实现代码

题目描述

  • 给定一个字符串S,以及一个模式串P,所有字符串中只包含大小写英文字母以及阿拉伯数字。
  • 模式串P在字符串S中多次作为子串出现。
  • 求出模式串P在字符串S中所有出现的位置的起始下标。

输入格式

  • 第一行输入整数N,表示字符串P的长度。
  • 第二行输入字符串P
  • 第三行输入整数M,表示字符串S的长度。
  • 第四行输入字符串S

输出格式

  • 共一行,输出所有出现位置的起始下标(下标从0开始计数),整数之间用空格隔开。

数据范围

  • 1 ≤ N ≤ 10^5
  • 1 ≤ M ≤ 10^6

基本思路

  • KMP算法是一个经典的字符串匹配算法。字符串匹配如果使用蛮力算法实现,则最坏情况下的时间复杂度是O(m*n),其中mn分别是母串和子串的长度;KMP算法将该时间复杂度优化到了O(m+n),可以说实现了一个重大的飞跃。
  • KMP算法可以分为求next数组和基于next数组的字符串匹配两部分,其中前一部分会更加困难一些。
  • 在字符串匹配部分,采用的方法是一种双指针算法,两个指针分别指向母串和子串中的一个待匹配字符。每次都比较两个指针所指向的字符,如果字符相同(发生匹配的情况),则同时将指针移动到下一个位置;如果字符不同(发生失配的情况),则进行分情况讨论:
    • 对于子串的首字符失配,则直接将母串的指针移动到下一个位置;
    • 对于子串的非首字符失配,则根据已经得到的next数组,修改子串指针所指向的位置,避免从头开始匹配带来的冗余消耗。
  • next数组的求解是KMP算法最困难的部分,下面进行介绍:
    • next数组中的每一个元素的值,表示从字符串的首个字符开始到当前元素对应的字符结束所构成的子串的最长相同前后缀的长度。注意,这里的最长相同前后缀不包含字符串本身。
    • next数组的首元素需要手动进行初始化,设置其值为0
    • next数组的构建同样是基于双指针算法完成的。起初,分别设置两个指针指向字符串的第一个字符和第二个字符,之后这两个指针都会向后移动。
      • 当两个指针指向的字符相同时,则当前右指针对应的字符的next数组元素值就是在上一个元素的next数组元素值上自增1
      • 当两个指针指向的字符不同时,则说明当前的最长相同前后缀分别加上左右指针所指向的字符后,无法继续构成相同的前后缀,因此就需要尝试寻找更短子串构成最长相同前后缀,即代码中的prefix_length = ne[prefix_length - 1]部分。这一部分理解起来比较复杂,因此下面通过一个实例进行介绍。
      • 假设需要构建对应的next数组的字符串是ABACABAB,且当前已经完成匹配的部分是第一个到第三个字符ABA和第五个到第七个字符ABA,第七个字符A对应的next数组值是3。现在左指针指向了第四个字符C,右指针指向了第八个字符B,显然两者不同,因此左右两边的字符串增加了新字符后,ABACABAB显然不是相同的前后缀,因此我们需要使用别的方式计算由这八个字符构成的字符串的最长相同前后缀。
      • 直观上我们发现,如果需要构建最长相同前后缀,那么就需要两个条件:加入的新字符完全相同、加入新字符前的前缀和后缀完全相同。这样,就可以产生一个巧妙的构思:既然当前字符串的最长相同前后缀分别增加一个新字符后无法继续构成最长相同前后缀,那么我们能不能退而求其次,不是使用当前的最长相同前后缀,而是第二长相同前后缀,来构建下一个最长相同前后缀呢?这是因为,第二长相同的前后缀也满足加入新字符前的前缀和后缀完全相同,并且修改了前缀和后缀后,前缀的下一个字符也会随着前缀更改一次,说不定就可以和右指针新插入的字符相同了。
      • 例如,对于ABACABA,最长的相同前后缀是ABA(前三个字符和后三个字符),但是最长的相同前后缀再增加第八个字符B后,构成的前后缀分别是ABACABAB,显然不相同。但是,如果使用第二长前后缀A(即第一个字符和第七个字符),增加一个字符B后,仍然可以构成相同前后缀AB。因此,在字符串当前的最长相同前后缀无法继续构成下一步的最长相同前后缀时,我们可以使用第二长的相同前后缀继续进行尝试,如果还不行则使用第三长的相同前后缀…直到找到可以继续构成相同前后缀的相同前后缀或确定不存在可以构成相同前后缀的相同前后缀(因为一个字符串的相同前后缀的个数是有限的)。现在的问题就是如何找出一个字符串的第二长相同前后缀。
      • 对于字符串ABABABA,其最长相同前后缀是ABA,包含三个字符,那么第二长的相同前后缀如果存在,则一定只包含两个字符或者一个字符,所以我们需要比较该字符串的前两个字符和后两个字符,以及第一个字符和最后一个字符。由于我们已经知道了ABA是最长相同前后缀,所以后两个字符实际上就是第二个和第三个字符,最后一个字符实际上就是第三个字符,因此问题就转换为了比较该字符串的前两个字符和第二个第三个字符,以及字符串的第一个字符和第三个字符是否相同的问题,其实也就是前三个字符构成的字符串ABA的最长相同前后缀问题!
      • 由于我们求解next数组的过程是顺序进行的,因此我们已经计算出了前三个字符ABA对应的next数组元素分别为001,因此我们可以得出对于ABA,其最长相同前后缀的长度为1,因此对于字符串ABABABA,其第二长相同前后缀的长度也是1。因此,在最长相同前后缀ABA无法继续构成最长相同前后缀时,我们使用第二长的相同前后缀尝试继续构建最长相同前后缀,构建出了AB,成功!

实现代码

#include <iostream>
using namespace std;// 创建一个静态数组ne作为next数组
const int n = 1e5 + 10;
int ne[n];// 获取next数组的函数
// 传入的参数分别是字符串的长度N和字符串P
// 函数根据传入的参数计算next数组
void get_next(int N, const string& P)
{// 首先将第一个元素对应的next值设置为0ne[0] = 0;// 定义并初始化两个指针int prefix_length = 0, j = 1;// 通过循环的方式求解next数组,直到完成对字符串的遍历while(j < N){// 情况1:两个指针所指向的字符相同,则说明最长相同前后缀可以同时自增,并记录结果if(P[prefix_length] == P[j]){++ prefix_length;ne[j] = prefix_length;++ j;}else{// 情况2:两个指针所指向的字符不同且第一个指针并不是指向字符串首字符,则根据next数组找出次最长相同前后缀if(prefix_length != 0) prefix_length = ne[prefix_length - 1];// 情况3:两个指针所指向的字符不同,且第一个指针指向字符串首元素,则直接将此处记录为0即可else{ne[j] = 0; ++ j;}}}
}// 进行KMP字符串匹配的函数
// 传入的参数分别是子串的长度N、子串P、母串的长度M、母串S
// 函数会在控制台输出子串P在母串S中所有出现位置的首字符下标
void KMP_search(int N, const string& P, int M, const string& S)
{// 首先根据子串P获取其对应的next数组get_next(N, P);// 定义并初始化母串和子串的指针int pp = 0, sp = 0;// 通过循环进行字符串匹配,当超出母串长度时停止循环while(sp < M){// 情况1:当前母串和子串的对应字符相同,则指针同时自增比较下一个元素if(P[pp] == S[sp]) {++ pp; ++ sp;}else{// 情况2:当前母串和子串的对应字符不同,且不是发生在子串第一个元素,则基于next数组修改子串指针if(pp != 0) pp = ne[pp - 1];// 情况3:当前母串和子串的对应字符不同,且发生在子串的第一个元素,则直接将母串指针自增else ++ sp;}// 每一次当子串遍历完成一次,则输出一次出现下标,并基于next数组修改子串指针if(pp == N){cout << sp - pp << " ";pp = ne[pp - 1];}}
}int main(void)
{int N, M;string P, S;cin >> N >> P >> M >> S;KMP_search(N, P, M, S);return 0;
}

相关文章:

算法刷题笔记 KMP字符串(C++实现,并给出了求next数组的独家简单理解方式)

文章目录 题目描述基本思路实现代码 题目描述 给定一个字符串S&#xff0c;以及一个模式串P&#xff0c;所有字符串中只包含大小写英文字母以及阿拉伯数字。模式串P在字符串S中多次作为子串出现。求出模式串P在字符串S中所有出现的位置的起始下标。 输入格式 第一行输入整数…...

SpringCloud架构师面试

一、微服务是什么 1、基本概念 微服务是一种架构风格&#xff08;区别于单体架构、垂直架构、分布式架构、SOA架构&#xff09;&#xff0c;应用程序被划分为更小的、流程驱动的服务。 2、微服务的特征 轻量化&#xff1a;将复杂的系统或者服务进行纵向拆分&#xff0c;每个…...

C语言 | Leetcode C语言题解之第228题汇总区间

题目&#xff1a; 题解&#xff1a; char** summaryRanges(int* nums, int numsSize, int* returnSize) {char** ret malloc(sizeof(char*) * numsSize);*returnSize 0;int i 0;while (i < numsSize) {int low i;i;while (i < numsSize && nums[i] nums[i …...

入职前回顾一下git-01

git安装 Linux上安装git 在linux上建议用二进制的方式来安装git&#xff0c;可以使用发行版包含的基础软件包管理工具来安装。 红帽系 sudo yum install gitDebian系 sudo apt install gitWindows上安装git 去官网下载和操作系统位数相同的安装包.或者可以直接安装GitHub…...

this指向解析

先看题目&#xff1a; 第一题&#xff1a; var name window var person1 { name: person1, show1: function () { console.log(this.name) }, show2: () > console.log(th show3: function () { return function () { …...

学习小记-Nacos的服务注册与发现原理

服务注册&#xff1a; 当一个服务实例启动时&#xff0c;它会向 Nacos 服务器注册自己的信息&#xff0c;包括 IP 地址、端口号、元数据&#xff08;如服务版本、区域信息等&#xff09;。服务实例使用 Nacos API 发送注册请求&#xff0c;Nacos 服务器接收请求并存储服务实例信…...

视频号矩阵系统源码,实现AI自动生成文案和自动回复私信评论,支持多个短视频平台

在当今短视频蓬勃发展的时代&#xff0c;视频号矩阵系统源码成为了自媒体人争相探索的宝藏。这一强大的技术工具不仅能帮助我们高效管理多个短视频平台&#xff0c;更能通过AI智能生成文案和自动回复私信评论&#xff0c;为自媒体运营带来前所未有的便利与效率。 一、视频号矩…...

[Spring] SpringBoot基本配置与快速上手

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…...

tomcat的优化、动静分离

tomcat的优化 tomcat自身的优化 tomcat的并发处理能力不强&#xff0c;大项目不适应tomcat做为转发动态的中间件&#xff08;k8s集群&#xff0c;pytnon rubby&#xff09;&#xff0c;小项目会使用&#xff08;内部使用的&#xff09;动静分离 默认配置不适合生产环境&…...

Python与自动化脚本编写

Python与自动化脚本编写 Python因其简洁的语法和强大的库支持&#xff0c;成为了自动化脚本编写的首选语言之一。在这篇文章中&#xff0c;我们将探索如何使用Python来编写自动化脚本&#xff0c;以简化日常任务。 一、Python自动化脚本的基础 1. Python在自动化中的优势 Pyth…...

树与二叉树

前言&#xff1a; 树这个结构想必在日常生活中很常见到&#xff0c;而现在要介绍的是一种独特的数据结构--二叉树。 1.树 &#xff08;1&#xff09;定义&#xff1a; 是一种非线性结构&#xff0c;有一个特殊的节点叫做根节点&#xff0c;树没有前驱节点&#xff1b;树是递…...

SpringBoot+Vue实现简单的文件上传(Excel篇)

SpringBootVue实现简单的文件上传 1 环境 SpringBoot 3.2.1&#xff0c;Vue 2&#xff0c;ElementUI 2 页面 3 效果&#xff1a;只能上传xls文件且大小限制为2M&#xff0c;选择文件后自动上传。 4 前端代码 <template><div class"container"><el…...

科研绘图系列:R语言金字塔图(pyramid plot)

介绍 金字塔图(Pyramid chart)是一种用于展示人口统计数据的图表,特别是用于展示不同年龄段的人口数量。这种图表通常用于展示人口结构,比如性别和年龄的分布。 特点: 年龄分层:金字塔图按年龄分层,每一层代表一个年龄组。性别区分:通常,男性和女性的数据会被分别展…...

Tomcat多实例

一、Tomcat多实例 Tomcat多实例是指在同一台服务器上运行多个独立的tomcat实例&#xff0c;每个tomcat实例都具有独立的配置文件、日志文件、应用程序和端口&#xff0c;通过配置不同的端口和文件目录&#xff0c;可以实现同时运行多个独立的Tomcat服务器&#xff0c;每个服务…...

前端Vue组件化实践:自定义加载组件的探索与应用

在前端开发领域&#xff0c;随着业务逻辑复杂度的提升和系统规模的不断扩大&#xff0c;传统的开发方式逐渐暴露出效率低下、维护困难等问题。为了解决这些挑战&#xff0c;组件化开发作为一种高效、灵活的开发模式&#xff0c;受到了越来越多开发者的青睐。本文将结合实践&…...

半小时获得一张ESG入门证书【详细中英文笔记一】

前些日子&#xff0c;有朋友转发了一则小红书的笔记给我&#xff0c; 标题是《半小时获CFI官方高颜值免费证书 ESG认证》。这对考证狂魔的我来说&#xff0c;必然不能错过啊&#xff0c;有免费的羊毛不薅白不薅。 ESG课程的 CFI 官方网址戳这里&#xff1a;CFI 于是信心满满的…...

类形断言和和类型推导的区别是什么?

类型断言&#xff08;Type Assertion&#xff09;和类型推导&#xff08;Type Inference&#xff09;在TypeScript中的区别 如下&#xff1a; 定义&#xff1a; 类型断言&#xff1a;是程序员明确指定一个值的类型&#xff0c;即允许变量从一种类型更改为另一种类型。它不会进行…...

Spring-Spring、IoC、DI、注解开发

1、Spring是什么 Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)。 Spring整体架构 Spring优点&#xff1a; Spring属于低侵入设计。IOC将对象之间的依赖关系交给Spring,降低组件之间的耦合&#xff0c;实现各个层之间的解耦&#xff0c;让我们更专注于业务…...

Facebook的未来蓝图:从元宇宙到虚拟现实的跨越

随着科技的不断演进和社会的数字化转型&#xff0c;虚拟现实&#xff08;VR&#xff09;和增强现实&#xff08;AR&#xff09;作为下一代计算平台正逐渐走进人们的视野。作为全球领先的科技公司之一&#xff0c;Facebook正在积极探索并推动这一领域的发展&#xff0c;以实现其…...

Redis6.2.1版本集群新加副本

测试数据 通过redis-benchmark生成测试数据 ./bin/redis-benchmark -h 172.31.4.18 -p 6381 -a Redis_6.2.1_Sc --cluster -t set -d 128 -n 10000000 -r 100000000 -c 200新加节点 172.31.4.18:6381> AUTH Redis_6.2.1_Sc OK172.31.4.18:6381> cluster meet 172.31.4…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

SkyWalking 10.2.0 SWCK 配置过程

SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外&#xff0c;K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案&#xff0c;全安装在K8S群集中。 具体可参…...

微信小程序之bind和catch

这两个呢&#xff0c;都是绑定事件用的&#xff0c;具体使用有些小区别。 官方文档&#xff1a; 事件冒泡处理不同 bind&#xff1a;绑定的事件会向上冒泡&#xff0c;即触发当前组件的事件后&#xff0c;还会继续触发父组件的相同事件。例如&#xff0c;有一个子视图绑定了b…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

vscode(仍待补充)

写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh&#xff1f; debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

vue3 字体颜色设置的多种方式

在Vue 3中设置字体颜色可以通过多种方式实现&#xff0c;这取决于你是想在组件内部直接设置&#xff0c;还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法&#xff1a; 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...