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

LeetCode28.找出字符串中第一个匹配项的下标

28.找出字符串中第一个匹配项的下标

目录

  • 28.找出字符串中第一个匹配项的下标
    • 题目描述
    • 解法一:朴素的模式匹配
    • 解法二:KMP算法
      • KMP解决的问题类型
      • 最长公共前后缀
      • KMP算法过程
      • next数组的构建
      • 代码实现

题目描述

给你两个字符串haystackneedle,请你在haystack字符串中找出needle字符串的第一个匹配项的下标(下标从0开始)。

如果needle不是haystack的一部分,则返回-1

在这里插入图片描述

解法一:朴素的模式匹配

第一种容易想到的方法就是暴力求解法,也叫做朴素的模式匹配:

简单的来说就是,从主串hyastack和子串needle的第一个字符开始,将两字符串的字符一一对比,如果出现某个字符不匹配,主串haystack回溯到第二个字符,子串needle回溯到第一个字符再进行一一对比,如果再次出现某个字符不匹配,则主串回溯到第三个字符,子串回溯到第一个字符…以此类推,直到子串t的字符全部匹配成功。

这道题目最为直观的解法是:依次枚举haystack中的每个字符作为[发起点],每次从原串(haystack)的[发起点]和匹配串(needle)的[首位]开始尝试匹配

  • 匹配成功:返回本次匹配的原串的[发起点]
  • 匹配失败:枚举原串的下一个[发起点],重新尝试匹配
    public int strStr(String haystack, String needle) {int n = haystack.length();int m = needle.length();for(int i=0;i+m<=n;i++){boolean flag = true;for(int j=0;j<m;j++){if(haystack.charAt(i+j)!=needle.charAt(j)){flag = false;break;}}if(flag){return i;}}return -1;}

解法二:KMP算法

当出现经典的字符串匹配时,我们选择使用KMP算法。

KMP解决的问题类型

kmp算法的作用是在一个已知字符串中查找子串的位置,也叫做串的模式匹配。

比如主串s=“university”,子串t=“sit”。现在我们要找到子串t在主串s中的位置,这点相信大家很容易就看出来了,是在第七个位置。

当然,在字符串非常少的时候,“肉眼观察法”不失为一个好方法,但如果要你在一千行一万行文本中找一个单词,我觉得一般人都找不到。

第一种容易想到的方法就是刚刚的解法一,暴力求解法,也叫做朴素的模式匹配

简单的来说就是,从主串s和子串t的第一个字符开始,将两字符串的字符一一对比,如果出现某个字符不匹配,主串s回溯到第二个字符,子串t回溯到第一个字符再进行一一对比,如果再次出现某个字符不匹配,则主串s回溯到第三个字符,子串s回溯到第一个字符…以此类推,直到子串t的字符全部匹配成功。

但是这个方法真的很慢,因为求一个子串的位置需要太多步骤,而且很多步骤根本没必要。

这种暴力解法在最好的情况下算法的时间复杂度为O(n),即子串的n个字符正好等于主串的前n个字符,而最坏的情况下时间复杂度为O(n*m)。但是好在这种算法的空间复杂度为O(1),即不消耗空间而消耗时间。

下面进入正题,KMP算法是如何优化这些步骤的。

其实KMP算法的主要思想就是,牺牲空间换时间

我们回头看一遍解法一的暴力方式,为什么这么慢呢?是因为我们回溯的步骤太多了,所以我们应该减少回溯的次数。

怎样做呢?比如上面第一个图:当字符’d’与’g’不匹配,我们保持主串的指向不变

主串依然指向’d’,而把子串进行回溯,让’d’与子串中’g’之前的字符再进行比对。

如果字符匹配,则主串和子串字符同时右移。

至于子串回溯到哪个字符,这个问题我们先放一放。

最长公共前后缀

这里提出一个概念:字符串的最长相等前缀和后缀

举个例子

字符串abcdab

前缀的集合:{a,ab,abc,abcd,abcda}

后缀的集合:{b,ab,dab,cdab,bcdab}

此时最长的公共前后缀就是ab

OK,现在我们已经会求一个字符串的前缀,后缀,以及公共前后缀了,这个概念很重要。

之前留了一个问题,子串回溯到哪个字符,现在可以着手解决了

KMP算法过程

现在我们看一个图:第一个长条代表主串,第二个长条代表子串,红色部分代表两串中已匹配的部分

绿色和蓝色部分分别代表主串和子串中不匹配的字符

在这里插入图片描述

在这里插入图片描述

现在发现了不匹配的地方,我们根据KMP算法的思想,保持主串位置不动,将子串向后移,现在我们要解决的,就是移动多少的问题

之前提到的最长公共前后缀的概念有用处了。

因为红色部分,即已经匹配的部分也会有最长相等前后缀,如下图

在这里插入图片描述

在这里插入图片描述

我们发现,之前“abcab”红色部分的最长公共前后缀为“ab”,所以,我们把前缀“ab”和后缀“ab”都标成灰色

子串移动的结果就是让子串的红色部分最长相等前缀和主串红色部分最长相等后缀对齐

在这里插入图片描述

在这里插入图片描述

这一步弄懂了,KMP算法的精髓我们就掌握了,接下来的流程就是一个简单的循环过程。

事实上,每一个字符前的字符串都有最长相等前后缀,而且最长相等前后缀的长度是我们移位的关键,所以我们单独使用一个next数组存储子串的最长相等前后缀的长度,而且next数组的数值只与子串本身有关。

所以,next[i]=j的含义是:下标为i的字符前的字符串最长相等前后缀的长度为j。

我们可以算出上图中子串“abcabcmn”的next数组为next[0]=-1(前面没有字符串单独处理)

字符abcabcmn
下标i01234567
next[i]-10001230

再看一眼刚刚是哪里出现了不匹配

在这里插入图片描述

即s[5]!=t[5]

我们把子串移动,也就是让s[5]与t[5]前面字符串的最长相等前缀的后一个字符再比较,而next[5] = 2,所以我们让子串t的第三个字符和刚刚主串的位置对齐开始比较

在这里插入图片描述

以此类推,直到将子串匹配完为止

这里我们可以总结一下,next数组的作用:

  • 1、next[i]的值表示下标为i的字符前的字符串的最长相等先后缀的长度
  • 2、表示该处字符不匹配时应该回溯到的字符的下标

next数组的构建

接下来,我们来看看next数组是如何被预设出来的。

假设有匹配串aaabbab,对应的next数组构建过程如下

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

代码实现

    public int strStr(String haystack, String needle) {if(needle.isEmpty()){return 0;}//分别读取原串和匹配串的长度int n = haystack.length(),m = needle.length();//原串和匹配串前面都加空格,使其下标从1开始haystack = " "+haystack;needle = " "+needle;//转成字符数组char[] s = haystack.toCharArray();char[] p = needle.toCharArray();//构建next数组,数组长度为匹配串的长度(这是因为next数组是和匹配串相关的)int[] next =new int[m+1];//构造过程,i=2,j=0开始,i小于等于匹配串的长度for(int i=2,j=0;i<=m;i++){//匹配不成功的话,j=next[j]while(j>0&&p[i]!=p[j+1]){j = next[j];}//匹配成功的话,先让j++if(p[i]==p[j+1]){j++;}//更新next[i],结束本次循环,i++next[i] = j;}//匹配过程,i=1,j=0开始,i小于等于原串长度for(int i=1,j=0;i<=n;i++){//匹配不成功 j=next(j)while(j>0&&s[i]!=p[j+1]){j=next[j];}//匹配成功的话,先让j++,结束本次循环后i++if(s[i]==p[j+1]){j++;}//整一段匹配成功,直接返回下标if(j==m){return i-m;}}return -1;}

相关文章:

LeetCode28.找出字符串中第一个匹配项的下标

28.找出字符串中第一个匹配项的下标 目录 28.找出字符串中第一个匹配项的下标题目描述解法一&#xff1a;朴素的模式匹配解法二&#xff1a;KMP算法KMP解决的问题类型最长公共前后缀KMP算法过程next数组的构建代码实现 题目描述 给你两个字符串haystack和needle&#xff0c;请…...

爬虫009_字符串高级_替换_去空格_分割_取长度_统计字符_间隔插入---python工作笔记028

然后再来看字符串的高级操作 取长度 查找字符串下标位置 判断是否以某个字符,开头结尾 计算字符出现次数 替换...

Windows 安装Tensorflow2.1、Pycharm开发环境

文章目录 1、安装anaconda2、安装Tensoflow2.1、创建虚拟环境2.2、安装Tensorflow依赖2.3、验证Tensorflow是否成功 3、配置pycharm环境4、错误记录 1、安装anaconda https://www.anaconda.com/download 打开命令行工具&#xff0c;出现base就表示安装成功了&#xff0c;表示当…...

【javaScript】数组的常用方法(自用记忆版)

目录 一、操作方法 增 push() unshift() splice() concat() 删 pop() shift() splice() slice() 改 splice() 查 indexOf() includes() find() 二、排序方法 reverse() sort() 三、转换方法 join() ​​​​​​四、迭代方法 some() every() forEach…...

全新二开美化版UI好看的社区源码下载/反编译版

2023全新二开美化版UI精美的社区源码下载/反编译版 之前我分享过Rule原版&#xff0c;相信大家已经有很多人搭建好了。这次我要分享的是RuleAPP的二开美化版&#xff08;请尊重每个作者的版权&#xff09;&#xff0c;这个版本没有加密&#xff0c;可以进行反编译&#xff0c;…...

Docker 发布一个springboot项目

文章目录 1、新建SpringBootDemo项目并打包2、使用Dockerfile打包&#xff08;基础用法&#xff09;进一步maven源码打包法 3、更进一步&#xff08;maven插件打包&#xff09;docker-maven-pluginspring-boot-maven-plugin前提条件本地环境配置项目环境配置maven插件打包运行校…...

办公信息系统安全基本技术要求

范围 本标准规定了办公信息系统的安全基本技术要求。 本标准适用于指导党政部门的办公信息系统建设&#xff0c;包括在系统设计、产品采购、系统集成等方面应遵循的基本原则&#xff0c;以及应满足的基本技术要求。涉密办公信息系统的建设管理应依据相关国家保密法规和标准要…...

有效管理IT问题的5个原则

问题管理就是发现未知的、隐藏的问题&#xff0c;这是根本原因&#xff0c; 这是您 IT 帮助台无穷无尽的工单来源。当您实施有效的 问题管理&#xff0c;您的 IT 团队可以超越消防模式&#xff0c;专注于战略 IT 目标。以下是可以帮助您实现一流问题管理的五个原则&#xff1a;…...

【MongoDB】解决ProxmoxVE下CentOS7虚拟机安装MongoDB6后启动失败的问题

目录 安装步骤: 2.1 配置yum源 2.2 安装MongoDB 2.3 启 动MongoDB ProxmoxVE上新装的CentOS7.4虚拟机,安装MongoDB6。 安装步骤: 2.1 配置yum源 # 创建mongodb yum源(https://www.mongodb.co...

MySQL 事务原理:事务概述、隔离级别、MVCC

文章目录 一、事务1.1 事务概述1.2 事务控制语句1.3 ACID特性 二、隔离级别2.1 隔离级别的分类2.1.1 读未提交&#xff08;RU&#xff09;2.1.2 读已提交&#xff08;RC&#xff09;2.1.3 可重复读&#xff08;RR&#xff09;2.1.4 串行化 2.2 命令2.3 并发读异常2.3.1 脏读2.3…...

useEffect从入门到入土

副作用是相对于纯函数概念来说的&#xff0c; 除事件回调处理副作用&#xff0c;其他副作用尽量放在useEffect中&#xff1b; 组件首次渲染、有依赖项更新&#xff08;Object.is方法判断&#xff09;时&#xff0c;该useEffect触发 jsx渲染完成后立马触发useEffect&#xff…...

第三章 图论 No.6负环之01分数规划与特殊建图方式

文章目录 裸题&#xff1a;904. 虫洞01分数规划&#xff1a;361. 观光奶牛特殊建图与01分数规划trick&#xff1a;1165. 单词环 裸题&#xff1a;904. 虫洞 904. 虫洞 - AcWing题库 // 虫洞是负权且单向边&#xff0c;道路是正权且双向边&#xff0c;题目较裸&#xff0c;判…...

九、Spring 声明式事务学习总结

文章目录 一、声明式事务1.1 什么是事务1.2 事务的应用场景1.3 事务的特性&#xff08;ACID&#xff09;1.4 未使用事务的代码示例1.5 配置 Spring 声明式事务学习总结 一、声明式事务 1.1 什么是事务 把一组业务当成一个业务来做&#xff1b;要么都成功&#xff0c;要么都失败…...

ResNet50卷积神经网络输出数据形参分析-笔记

ResNet50卷积神经网络输出数据形参分析-笔记 ResNet50包含多个模块&#xff0c;其中第2到第5个模块分别包含3、4、6、3个残差块 5049个卷积&#xff08;3463)*31和一个全连接层 分析结果为&#xff1a; 输入数据形状:[10, 3, 224, 224] 最后输出结果&#xff1a;linear_0 [10,…...

uniapp 微信小程序 封装公共的请求js(api版本)

一、新建api文件夹 在项目目录下创建api文件夹&#xff0c;内放files跟index.js文件夹&#xff0c;files文件夹内放每个页面对应的js请求接口 1、index.js /*** api接口的统一出口*/ const api {}; const requireComponent require.context(./files, false, /\.js$/) requi…...

格式化后数据恢复,教你3个实用方法!

“格式化后数据还能恢复吗&#xff1f;前几天因为我的电脑中了病毒&#xff0c;我不得不将它进行格式化操作。但是我电脑里有很多比较重要的文件&#xff0c;有什么方法可以帮我恢复电脑中的文件吗&#xff1f;求解答&#xff01;” 格式化是一种比较常见的数据清除方法&#x…...

LaTex使用技巧21:设置中文环境、字体、行间距和页边距

我在Overleaf上编写我的中文LaTex&#xff0c;设置了中文环境&#xff0c;字体、行间距以及页间距&#xff0c;记录一下方便以后查询。 使用中文环境命令为&#xff1a; \usepackage{xeCJK}可以使用Overleaf上支持的中文字体Fonts for CJK Chinese&#xff0c;设置字体的命令…...

【RabbitMQ】golang客户端教程3——发布订阅(使用fanout交换器)

发布订阅 在上一个教程中&#xff0c;我们创建了一个工作队列。工作队列背后的假设是每个任务只传递给一个工人。在这一部分中&#xff0c;我们将做一些完全不同的事情——我们将向多个消费者传递一个消息。这就是所谓的“订阅/发布模式”。 为了说明这种模式&#xff0c;我们…...

图像处理学习笔记

图像处理的流程&#xff1a;获取图像-分割区域-特征提取。 嵌入式工业读码器 &#xff1a;包括DM码、QR码、vericode码 Blob分析与形态学 1.Blob区域是Blobs这一数据类型在halcon中的一种贴切的表达形式。 采集图像-区域分割&#xff0c;最后通过特征&#xff08;如圆度、面积、…...

87端口无法访问-GoogleChrome非安全端口列表

以下为Google Chrome 默认非安全端口列表 平时我们服务器尽量不要开启这些端口&#xff0c;会产生访问不了的错误&#xff01; 1, // tcpmux7, // echo9, // discard11, // systat13, // daytime15, // netstat17, // qotd19, // chargen20, // ftp data…...

Linux链表操作全解析

Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表&#xff1f;1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

linux 错误码总结

1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

Caliper 配置文件解析:fisco-bcos.json

config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...

Linux系统部署KES

1、安装准备 1.版本说明V008R006C009B0014 V008&#xff1a;是version产品的大版本。 R006&#xff1a;是release产品特性版本。 C009&#xff1a;是通用版 B0014&#xff1a;是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存&#xff1a;1GB 以上 硬盘&#xf…...

面试高频问题

文章目录 &#x1f680; 消息队列核心技术揭秘&#xff1a;从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"&#xff1f;性能背后的秘密1.1 顺序写入与零拷贝&#xff1a;性能的双引擎1.2 分区并行&#xff1a;数据的"八车道高速公路"1.3 页缓存与批量处理…...

基于开源AI智能名片链动2 + 1模式S2B2C商城小程序的沉浸式体验营销研究

摘要&#xff1a;在消费市场竞争日益激烈的当下&#xff0c;传统体验营销方式存在诸多局限。本文聚焦开源AI智能名片链动2 1模式S2B2C商城小程序&#xff0c;探讨其在沉浸式体验营销中的应用。通过对比传统品鉴、工厂参观等初级体验方式&#xff0c;分析沉浸式体验的优势与价值…...

Selenium 查找页面元素的方式

Selenium 查找页面元素的方式 Selenium 提供了多种方法来查找网页中的元素&#xff0c;以下是主要的定位方式&#xff1a; 基本定位方式 通过ID定位 driver.find_element(By.ID, "element_id")通过Name定位 driver.find_element(By.NAME, "element_name"…...