图解KMP算法——字符串搜索

news2024/11/22 6:18:03

原文:最初发布地址

一、问题描述

来源:Leetcode

难度:中等

给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回  -1 。

示例一

输入:haystack = "sadbutsad", needle = "sad"

输出:0

解释:"sad" 在下标 0 和 6 处匹配。

第一个匹配项的下标是 0 ,所以返回 0 。

示例二

输入:haystack = "leetcode", needle = "leeto"

输出:-1

解释:"leeto" 没有在 "leetcode" 中出现,所以返回 -1 。

二、问题分析

S代表用于匹配的字符串

T代表模式字符串

先通过一个例子来看子串匹配过程 

判断 T是否是S的子串

S = AAAAAAB 
T = AAAAB

人可以一眼看出T是S的子串,但是计算机只能通过一点点匹配来判断

每次都从头开始进行匹配,如果不符合,S的下标s++,T的小标t=0,然后继续比较,但是可以看到这两个字符串有很多重复的部分,这部分有没有办法进行跳过呢,这就是这篇文章的重点,KMP算法,通过KMP算法就可以实现跳过重复部分,对字符串匹配进行加速

Knuth–Morris–Pratt(KMP)算法是一种改进的字符串匹配算法,它的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。它的时间复杂度是 O(m+n)。

KMP的核心思路

KMP算法的关键在于一个称为“部分匹配表”(也叫“前缀表”)的数据结构。这个表基于模式串本身构建,用来记录模式串每个位置前的子串中,前缀和后缀最长共有元素的长度。这里的“前缀”指的是除了最后一个字符外,一个字符串的全部头部组合;“后缀”指的是除了第一个字符外,一个字符串的全部尾部组合。

例如上面的例题,对模式串计算前缀表得到

计算过程是

又例如 abcdabcf

计算过程是

一句话就是找公共前缀,只能从前面的子串找

利用这个前缀表,来加速字符串匹配

S = AAAAAAB 
T = AAAAB
# 前缀表 next
[0,0,1,2,3]
  • S[i]和T[j]做比较,相等,则i++,j++,不等则j=next[j]

  • 当i=4,j=4时,S[i]!=T[j], j=next[4]=3, 可以接着从3开始比较

  • S[4]=T[3],i++,j++, 重复上述过程,过程如图,删除线表示跳过的比较

再例如

S=a  b  a  b  e  a  b  a  b  f
T=a  b  a  b  f 
 [0  0  0  1  2] # 前缀表

计算过程

【细节处理见代码】

三、代码实现

func buildNext(str string) []int {
  next := make([]int, len(str))
  // j=1是 为了保证字符串错开,例如 abc ,a应该不能直接跟a比较,那样没有意义,应该是 a跟b比较
  j := 1
  k := 0
  for j < len(str)-1 {
    if str[j] == str[k] {
      k++
      j++ // 加1,是因为 j和 k相等,则说明j+1的前缀是 k
      next[j] = k
    } else {
​
      // 为什么只有为0,才能加1
      // 因为:如果k不为0,则可能,next[k]大于0,则当前的值,需要和str[k]进行比较,来得到next[k+1]的前缀
      // 例如 aabaaacth 的前缀长度数组是[0,0,1,0,1,2,2,0,0]
      // 如 str[2]=b和str[5]=a比较后,不等
      // 比较后k=2,j=5,
      // 加1: 则k=1,j=6, str[1]!=str[6],所以next[6]=0
      // 不加1,则j=5,k=1, str[j]==str[k],则next[5+1]=k+1,
      if k == 0 {
        j++
      }
      // 取next[k]的值赋给k,
      //相当于一个动态规划的缓存,用前面的值推出后续的值
      // 但是不像动态规划,能够直接给出推到公式
      k = next[k]
    }
​
  }
  return next
}
​
​
// 子串
func subStr(haystack string, needle string) int {
​
  N := buildNext(needle)
​
  L1 := len(haystack)
  L2 := len(needle)
​
  i := 0
  k := 0
​
  for i < L1 && k < L2 {
​
    if haystack[i] == needle[k] {
      k++
      i++
    } else {
      // k不等于0时,
      // 当前haystack[i]需要和模式串的[k]进行比较
      // 所以不能+1
      if k == 0 {
        i++
      }
​
      k = N[k]
​
    }
​
  }
  // 这里如果 模式串【needle】长度如果等于k,说明模式串是匹配串【haystack】的子串
  // 通过匹配串目前的下标减模式串的长度,即可得到第一个匹配的位置
  if k == L2 {
    return i - L2
  }
​
  return -1
}
​

代码验证:

力扣icon-default.png?t=N7T8https://leetcode.cn/leetbook/read/array-and-string/cm5e2/

必应百度谷歌

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

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

相关文章

2024年最新版云开发cms开通步骤,开始开发微信小程序前的准备工作,认真看完奥!

小程序官方有改版了&#xff0c;搞得石头哥不得不紧急的再新出一版&#xff0c;教大家开通最新版的cms网页管理后台 一&#xff0c;技术选型和技术点 1&#xff0c;小程序前端 wxml css JavaScript MINA原生小程序框架 2&#xff0c;数据库 云开发 云数据库 云…

SOTAX溶出测试系统PC触摸屏维修三部曲

SOTAX溶出测试系统作为一款广泛应用于制药行业的知名品牌&#xff0c;具有高精度、操作简便、稳定性好等特点。它适用于各种类型的药品研发和生产环节&#xff0c;为科研人员提供可靠的数据支持。瑞士SOTAX溶出仪是实验室中常用的设备&#xff0c;其触摸屏是用户交互的重要界面…

程序员读的经典著作有哪些?

一、程序员读的经典著作有哪些&#xff1f; 在编程的世界里&#xff0c;阅读经典著作不仅能够帮助我们深入理解编程的本质&#xff0c;也能为我们提供解决问题的新思路和方法。以下是几本被广大程序员推崇的经典著作&#xff0c;每本书都有其独特的价值和深远的影响。 1. 《代…

【C++】双指针算法:快乐数

1.题目 题目中一定要理解快乐数的含义&#xff0c;否则题目难度直逼困难。 在示例1中n19&#xff0c;经过几步操作后结果变成1。 那么示例2中n2是什么情况呢&#xff1a; 2->4->16->37->58->89->145->42->20->4(与前面的4形成闭环) 在计算机中in…

苍穹外卖day11 Apache ECharts 数据统计-图形报表

文章目录 前言一、Apache ECharts二、营业额统计1. 业务规则2. 接口设计3. 代码实现 三、用户统计1. 业务规则2. 接口设计3. 代码实现 四、订单统计1. 业务规则2. 接口设计3. 代码实现 五、销量排名Top101. 业务规则2. 接口设计3. 代码实现 前言 作为后端开发人员使用Echarts&…

【C++】类和对象④(类的默认成员函数:取地址及const取地址重载 | 再谈构造函数:初始化列表,隐式类型转换,缺省值)

&#x1f525;个人主页&#xff1a;Forcible Bug Maker &#x1f525;专栏&#xff1a;C 目录 前言 取地址及const取地址操作符重载 再谈构造函数 初始化列表 隐式类型转换 explicit关键字 成员变量缺省值 结语 前言 本篇主要内容&#xff1a;类的六个默认成员函数中…

算法训练营day15

一、层序遍历 参考链接7.2 二叉树遍历 - Hello 算法 (hello-algo.com) 层序遍历本质上属于广度优先遍历&#xff0c;也称广度优先搜索&#xff0c; BFS通常借助队列的先入先出的特性实现 参考链接102. 二叉树的层序遍历 - 力扣&#xff08;LeetCode&#xff09; 像这种较为…

Go语言并发控制

channel // cancelFn 数据通道关闭通知退出 func cancelFn(dataChan chan int) {for {select {case val, ok : <-dataChan:// 关闭data通道时&#xff0c;通知退出// 一个可选是判断data指定值时退出if !ok {fmt.Printf("Channel closed &#xff01;&#xff01;&…

前端页面助手 (vue)

快速开发页面&#xff08;图形化开发页面&#xff09; 自主编辑 然后自己也可以修改属性 最后导出页面即可 github地址 ;https://github.com/opentiny/tiny-engine

蚓链数字化营销系统与数字资产的关系

蚓链数字化营销系统是一种利用数字技术来实现营销目标的系统。它集成了多种数字营销工具和渠道&#xff0c;以收集、分析和利用客户数据&#xff0c;优化营销活动&#xff0c;并提高营销效果。 数字资产是一种新型的资产类别&#xff0c;它们以电子数据的形式存在&#xff0c;可…

前端开发攻略---实现发送手机验证码60s倒计时效果(手机号验证+按钮文字自定义显示+Vue2写法+Vue3写法)

1、演示 2、说明 1、为了便于演示&#xff0c;本示例将在3秒后就再次发送。您可以根据需要自定义此时间间隔。 2、采用最少的变量以满足需求&#xff0c;以减少内存占用。 3、不仅仅局限于按钮情况&#xff0c;也可应用于不禁用按钮的情况&#xff0c;以实现更多的扩展性。 4、…

02 VMware下载安装银河麒麟(Kylin)系统

02 VMware下载&安装银河麒麟&#xff08;Kylin&#xff09;系统 一、官网1、官网地址 二、下载1、官网下载&#xff08;1&#xff09;服务器操作系统&#xff08;2&#xff09;申请试用&#xff08;3&#xff09;产品试用申请&#xff08;4&#xff09;点击下载连接即可 2、…

Redis篇:缓存穿透以及解决方案

1.何为缓存穿透 缓存穿透 &#xff1a;缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在&#xff0c;这样缓存永远不会生效&#xff0c;这些请求都会打到数据库。 比如查询一个id 0的数据&#xff0c;这是在redis和数据库中肯定不存在的&#xff0c;这样就属于缓存穿…

vue2+vxe-table实现表格增删改查+虚拟滚动

vue2vxe-table实现表格增删改查虚拟滚动 使用的vxe-table版本&#xff1a;v3.x (vue 2.6 长期维护版) 完整代码 <template><div><vxe-toolbar ref"xToolbar" export :refresh"{query: findList}"><template #buttons><vxe-b…

【网络安全】在网络中如何对报文和发送实体进行鉴别?

目录 1、报文鉴别 &#xff08;1&#xff09;使用数字签名进行鉴别 &#xff08;2&#xff09;密码散列函数 &#xff08;3&#xff09;报文鉴别码 2、实体鉴别 鉴别(authentication) 是网络安全中一个很重要的问题。 一是要鉴别发信者&#xff0c;即验证通信的对方的确是…

富唯智能:打造未来机器人教育新标杆

随着科技的飞速发展&#xff0c;机器人教育正逐渐成为培养未来人才的重要领域。富唯智能&#xff0c;作为业内领先的机器人技术提供商&#xff0c;近日推出了一款全新的机器人教育实践平台系统&#xff0c;旨在为学生提供更加丰富、更具挑战性的学习体验。 该平台系统以AUBO-i5…

A Neural Span-Based Continual Named Entity Recognition Model

《A Neural Span-Based Continual Named Entity Recognition Model》------------AAAI’23 论文链接&#xff1a;https://arxiv.org/abs/2302.12200 代码&#xff1a;https://github.com/Qznan/SpanKL 当前问题&#xff1a; 1.现有的NER模型在适应新的实体类型时往往表现不佳…

基于CAPL的S19文件解析

&#x1f345; 我是蚂蚁小兵&#xff0c;专注于车载诊断领域&#xff0c;尤其擅长于对CANoe工具的使用&#x1f345; 寻找组织 &#xff0c;答疑解惑&#xff0c;摸鱼聊天&#xff0c;博客源码&#xff0c;点击加入&#x1f449;【相亲相爱一家人】&#x1f345; 玩转CANoe&…

【Qt 学习笔记】Qt常用控件 | 按钮类控件 | Check Box的使用及说明

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt常用控件 | 按钮类控件 | Check Box的使用及说明 文章编号&#xff…

js的算法-交换排序(快速排序)

快速排序 基本思想 快速排序的基本思想是基于分治法的&#xff1a;在待排序表L【1...n】中任意取一个元素p 作为枢轴&#xff08;或基准&#xff0c;通常取首元素&#xff09;。通过一趟排序将待排序表划分为独立的两部分L【1...k-1】和L【k1...n】;这样的话&#xff0c;L【1…