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

图解KMP算法

子串的定位操作通常称作串的模式匹配。你可以理解为在一篇英语文章中查找某个单词是否存在,或者说在一个主串中寻找某子串是否存在。

  1. 朴素的模式匹配算法

假设我们要从下面的主串S = "goodgoogle" 中,找到T = "google" 这个子串的位置。利用朴素的模式匹配算法我们通常需要下面的步骤。

(1):主串S第一位开始,S与T前三个字母匹配成功,但S第四个字母是d而T的是g。第一位匹配失败。如下图所示:浅绿色方块代表匹配成功,红色方块代表匹配失败。

(2):主串S从第二位开始,主串S首字母是o,要匹配的T的首字母是g。匹配失败:

(3):主串S第三位开始,主串S首字母是o,要匹配的T首字母是g,匹配失败:

(4):主串S第四位开始,主串S首字母是d,要匹配的T首字母是g,匹配失败:

(5):主串S第五位开始,S与T,6个字母全部匹配,匹配成功:

简单来说,朴素的模式匹配算法就是对主串的每一位字符作为子串的开头,与要匹配的子串进行比较,如果匹配不成功,则主串需要回溯到与子串开始匹配的位置的下一个位置。

当子串与主串有较多的相等的字符时,这种算法的效率是极其低下的。例如:

  1. KMP模式匹配算法

如果你忍受不了朴素的模式匹配算法。于是有三位前辈:D.E.Knuth,J.H.Morris和V.R.Pratt发表了一个模式匹配算法,可以大大避免重复遍历的情况,我们把他称之为克努特-莫里斯-普拉特算法,简称KMP算法。

在去理解KMP算法时你需要知道一个字符串的前缀和后缀是什么?

下面以字符串 "google" 为例:

他的前缀集合:{ "g", "go", "goo", "goog", "googl" }。

它的后缀集合:{ "e", "le", "gle", "ogle", "oogle" }。

2.1 KMP算法匹配方式

假设我们要在主串S = "ABBABBABABAAABABAAA" 中查找模式串T = "ABBABAABABAA" 中的位置。

(1):我们发现主串S和子串T的前5个字符均是匹配的,第6个字符是不匹配的。下一步我们肯定不能回溯到主串第二个字符的位置,不然就是在重走朴素的模式算法的道路了。我们观察发现:1:匹配了五个字符中,2:在模式串的左右两端有两个字符子串,他们是完全匹配的。我们称之为最长公共前后缀。

(2):移动模式串,使得原来模式串的前缀到达模式串的后缀的位置:

这样的移动可以使得重新开始匹配的位置之前的字符,均是匹配的,因为公共前后缀AB是匹配的,并且原来的5个字符也是匹配的,所以将模式串的前缀移动到后缀的位置,与主串的后缀是匹配的。这样移动可以省去部分字符的不必要匹配(这样移动的正确性我们稍后探讨)。

(3):我们发现模式串的移动与主串并没有关系,因为只是将模式串的前缀移动到了模式串的后缀的位置,并且已经匹配的字符与主串肯定是一样的撒。

这时我们对一般情况做出分析:

我们现在已经明白了为什么要移动模式串的前缀到后缀的位置,以及这样移动的正确性。

(4):现在我们可以继续匹配一下主串,理解上述的操作:

2.2 next数组的手动推导

上面的图解以及分析仅是一个形象化的过程,我们还得将其转化为计算机能够处理的方式。

emm,我们假设存储字符串是从下标为1的位置开始的呢!

下面以具体的例子:主串S = "ABABABAABABAAABABAA",模式串T = "ABABAAABABAA",来分析如何转化成计算机能够处理的方式。

上面分析过,在模式串的移动过程中,与主串是没有关系的,我们只需要分析模式串即可:

模式串的任何一个位置都可能与主串发生不匹配,我们就是要将不匹配之后模式串由前缀移动到后缀的过程抽象成计算机能处理的形式。

我们发现有这样的规律:next数组对应的值是最长的公共前后缀加1。模式串剩余的字符就不再画图分析了,

我们将上面的数组取名为next数组,例如:我们发现模式串在与主串比较时,下标为5的位置发生了不匹配,我们只需要查找next[5] 的值,值为:3,就知道下一步是将模式串的3号位与主串的当前位置进行比较。

2.3 next数组的代码分析

/// <summary>
/// 求解next数组
/// </summary>
/// <param name="s"> 模式串的首字符地址 </param>
/// <param name="n"> 模式串的字符个数 </param>
/// <param name="next"> next数组的首元素地址 </param>
void getNext(char* s, int n, int* next)
{//遍历模式串int i = 1;//为next数组赋值int j = 0;//第一个字符不匹配将编号赋值为0,代表从主串的下一个位置开始比较next[1] = 0;while (i < n){if (j == 0 || s[i] == s[j])next[++i] = ++j;elsej = next[j];}
}

现在我们先以一个具体的例子来看代码是否正确哈,假设模式串为:"abab":

next[1] 需要一开始就初始化的哈。用i遍历模式串时,如果j指向的字符与i指向的字符相等,进入if分支,说明最长公共前后缀需要增加1,为什么是增加1呢?因为i一次只能遍历一个字符嘛!如果i指向的字符与j指向的字符不相等的话,进入else分支,令j=next[j],即更新j的位置,将新的j指向的字符与i指向的字符进行比较,看是否相等,不相等继续令j=next[j],直到j指向的字符与i指向的字符相等或者说j为0。为什么j的更新是j=next[j]? 我们下面就来分析原因!

这下你就能理解求next数组的全部代码啦。

2.4 查找子串的完整代码

下面是字符串从下标为1开始存储时的查找子串的代码。只是以一个具体的例子实践了一下,你只要理解了思路就随便写代码的。这里的代码仅供参考,具体代码还需结合题目具体分析!

/// <summary>
/// 求解next数组
/// </summary>
/// <param name="s"> 模式串的首字符地址 </param>
/// <param name="n"> 模式串的字符个数 </param>
/// <param name="next"> next数组的首元素地址 </param>
void getNext(char* s, int n, int* next)
{//i用来遍历模式串,j用来为next数组赋值,并且查找最长公共前后缀int i = 1, j = 0;//next[1]需要直接赋值next[1] = 0;while (i < n){if (j == 0 || s[i] == s[j])next[++i] = ++j;elsej = next[j];}
}
int main()
{char a[10] = " abababde";char s[5] = " aba";int next[4];getNext(s, 3, next);//8是主串的长度哈for (int i = 1, j = 0; i <= 8; i++){while (j && a[i] != s[j+1])j = next[j];if (a[i] == s[j+1])j++;//3是模式串的字符个数,相等就代表找到了if (j == 3){//输出开始匹配的位置printf("%d ", i - j + 1);//更新j尝试找到更多解j = next[j];}}return 0;
}

下面就是字符串从0开始存储的代码,同样的哈,代码版本有很多,重要的是理解思路!

void getNext(char* s, int n, int* next)
{int i = 0, j = -1;next[0] = -1;while (i < n){if (j == -1 || s[i] == s[j])next[++i] = ++j;elsej = next[j];}
}int main()
{char a[9] = "abababde";char b[5] = "abab";int next[5];getNext(b, 4, next);for (int i = 0, j = 0; i < 8; i++){while (j && a[i] != b[j])j = next[j];if (a[i] == b[j])j++;if (j == 4){printf("%d ", i - j + 1);j = next[j];}}return 0;
}
  1. 小试牛刀

下面的题解法肯定不止一种,kmp算法也不一定是最简单的,但是阔以用来练习kmp算法的!

3.1 旋转字符串

796. 旋转字符串 - 力扣(LeetCode)
题目描述:给定两个字符串, s 和 goal。如果在若干次旋转操作之后,s 能变成 goal ,那么返回 true。
s 的 旋转操作 就是将 s 最左边的字符移动到最右边。
例如, 若 s = 'abcde',在旋转一次之后结果就是'bcdea' 。

用kmp解题的大致思路就是拼接一个字符串到s后面,将goal当成模式串,在拼接后的字符串中查找goal就行。

void getNext(char* neddle, int n,int*next)
{int i =0;int j =-1;next[0] = -1;while(i<n){if(j==-1||neddle[i]==neddle[j])next[++i] = ++j;elsej = next[j];}
}
bool rotateString(char * s, char * goal){int lens = strlen(s);int leng = strlen(goal);if(lens!=leng)return false;char*str = (char*)calloc(lens*2+1, sizeof(char));memmove(str,s,lens);memmove(str+lens,s,lens);int * next = (int*)malloc(sizeof(int)*(leng+1));getNext(goal, leng,next);for(int i = 0,j=0;i<lens*2;i++){while(j&&str[i]!=goal[j])j = next[j];if(str[i]==goal[j])j++;if(j==leng){return true;}}return false;
}

3.2 最长快乐前缀

1392. 最长快乐前缀 - 力扣(LeetCode)
题目描述:
「快乐前缀」 是在原字符串中既是 非空 前缀也是后缀(不包括原字符串自身)的字符串。
给你一个字符串 s,请你返回它的 最长快乐前缀。如果不存在满足题意的前缀,则返回一个空字符串 "" 。
void getNext(char*s,int n,int* next)
{int i = 0;int j = -1;next[0] = -1;while(i<n){if(j==-1||s[i]==s[j])next[++i] = ++j;elsej = next[j];}
}char * longestPrefix(char * s){int len = strlen(s);int next[len+1];getNext(s,len,next);int max = 0;if(next[len]>max)max = next[len];if(max==0)return "";else{char* ret = (char*)calloc(max+1,sizeof(char));for(int i = 0;i<max;i++){ret[i] = s[i];}return ret;}
}

相关文章:

图解KMP算法

子串的定位操作通常称作串的模式匹配。你可以理解为在一篇英语文章中查找某个单词是否存在&#xff0c;或者说在一个主串中寻找某子串是否存在。朴素的模式匹配算法假设我们要从下面的主串S "goodgoogle" 中&#xff0c;找到T "google" 这个子串的位置。…...

Java Map和Set

目录1. 二叉排序树(二叉搜索树)1.1 二叉搜索树的查找1.2 二叉搜索树的插入1.3 二叉搜索树的删除&#xff08;7种情况&#xff09;1.4 二叉搜索树和TreeMap、TreeSet的关系2. Map和Set的区别与联系2.1 从接口框架的角度分析2.2 从存储的模型角度分析【2种模型】3. 关于Map3.1 Ma…...

【C/C++ 数据结构】-八大排序之 冒泡排序快速排序

作者&#xff1a;学Java的冬瓜 博客主页&#xff1a;☀冬瓜的主页&#x1f319; 专栏&#xff1a;【C/C数据结构与算法】 分享&#xff1a;那我便像你一样&#xff0c;永远躲在水面之下&#xff0c;面具之后&#xff01; ——《画江湖之不良人》 主要内容&#xff1a;八大排序选…...

苹果ipa软件下载网站和软件的汇总

随着时间的流逝&#xff0c;做苹果版软件安装包下载网站和软件的渐渐多了起来。 当然&#xff0c;已经关站、停运、下架、倒闭的苹果软件下载网站和软件我就不说了&#xff0c;也不必多说那些关站停运下架倒闭的网站和软件了。 下面我统计介绍的就是苹果软件安装包下载网站和软…...

深度学习-【语义分割】学习笔记4 膨胀卷积(Dilated convolution)

文章目录膨胀卷积为什么需要膨胀卷积gridding effect连续使用三次膨胀卷积——1连续使用三次膨胀卷积——2连续使用三次膨胀卷积——3Understanding Convolution for Semantic Segmentation膨胀卷积 膨胀卷积&#xff0c;又叫空洞卷积。 左边是普通卷积&#xff0c;右边是膨胀…...

【10】SCI易中期刊推荐——工程技术-计算机:人工智能(中科院2区)

🚀🚀🚀NEW!!!SCI易中期刊推荐栏目来啦 ~ 📚🍀 SCI即《科学引文索引》(Science Citation Index, SCI),是1961年由美国科学信息研究所(Institute for Scientific Information, ISI)创办的文献检索工具,创始人是美国著名情报专家尤金加菲尔德(Eugene Garfield…...

模电计算反馈系数,有时候转化为计算电阻分压的问题

模电计算反馈系数&#xff0c;有时候转化为计算电阻分压的问题 如果是电压反馈&#xff0c;F的除数是Uo 如果是电流反馈&#xff0c;F的除数是Io 串联反馈&#xff0c;F的分子是Uf 并联反馈&#xff0c;F的分子是If 点个赞呗&#xff0c;大家一起加油学习&#xff01;...

专治Java底子差,不要再认为泛型就是一对尖括号了

文章目录一、泛型1.1 泛型概述1.2 集合泛型的使用1.2.1 未使用泛型1.2.2 使用泛型1.3 泛型类1.3.1 泛型类的使用1.2.2 泛型类的继承1.4 泛型方法1.5 泛型通配符1.5.1 通配符的使用1&#xff09;参数列表带有泛型2&#xff09;泛型通配符1.5.2 泛型上下边界1.6 泛型的擦除1.6.1 …...

PayPal轮询收款的那些事儿

想必做跨境电商独立站的小伙伴&#xff0c;对于PayPal是再熟悉不过了&#xff0c;PayPal是一个跨国际贸易的支付平台&#xff0c;对于做独立站的朋友来说跨境收款绝大部分都是依赖PayPal以及Stripe条纹了。简单来说PayPal跟国内的支付宝有点类似&#xff0c;但是PayPal它是跨国…...

【Linux】项目自动化构建工具——make/Makefile

目录 1.make与Makefile的关系 Makefile make 项目清理 clean .PHONY 当我们编写一个较大的软件项目时&#xff0c;通常需要将多个源文件编译成可执行程序或库文件。为了简化这个过程&#xff0c;我们可以使用 make 工具和 Makefile 文件。Makefile 文件可以帮助我们自动…...

成本降低90%,OpenAI正式开放ChαtGΡΤ

今天凌晨&#xff0c;OpenAI官方发布ChαtGΡΤ和Whisper的接囗&#xff0c;开发人员现在可以通过API使用最新的文本生成和语音转文本功能。OpenAI称&#xff1a;通过一系列系统级优化&#xff0c;自去年12月以来&#xff0c;ChαtGΡΤ的成本降低了90%&#xff1b;现在OpenAI用…...

hls.js如何播放m3u8文件(实例)?

HLS&#xff08;HTTP Live Streaming&#xff09;是一种视频流传输协议&#xff0c;是苹果推出的适用于iOS与macOS平台的流媒体传输协议。它将视频分割成若干个小段&#xff0c;每个小段大小一般为2~10秒不等&#xff0c;并通过HTTP协议进行传输。通过在每个小段之间插入若干秒…...

大数据平台建设方法论集合

文章目录从0到1建设大数据解决方案大数据集群的方法论数据集成方法论机器学习算法平台方法论BI建设的方法论云原生大数据的方法论低代码数据中台的方法论大数据SRE运维方法论批流一体化建设的方法论数据治理的方法论湖仓一体化建设的方法论数据分析挖掘方法论数字化转型方法论数…...

25- 卷积神经网络(CNN)原理 (TensorFlow系列) (深度学习)

知识要点 卷积神经网络的几个主要结构: 卷积层&#xff08;Convolutions&#xff09;: Valid :不填充&#xff0c;也就是最终大小为卷积后的大小. Same&#xff1a;输出大小与原图大小一致&#xff0c;那么N ​变成了​N2P. padding-零填充. 池化层&#xff08;Subsampli…...

把数组里面数值排成最小的数

问题描述&#xff1a;输入一个正整数数组&#xff0c;将它们连接起来排成一个数&#xff0c;输出能排出的所有数字中最小的一个。例如输入数组{12, 567}&#xff0c;则输出这两个能排成的最小数字12567。请给出解决问题的算法&#xff0c;并证明该算法。 思路&#xff1a;先将…...

云his系统源码 SaaS应用 基于Angular+Nginx+Java+Spring开发

云his系统源码 SaaS应用 功能易扩 统一对外接口管理 一、系统概述&#xff1a; 本套云HIS系统采用主流成熟技术开发&#xff0c;软件结构简洁、代码规范易阅读&#xff0c;SaaS应用&#xff0c;全浏览器访问前后端分离&#xff0c;多服务协同&#xff0c;服务可拆分&#xff…...

小红书场景营销怎么做?场景营销主要模式有哪些

小红书作为新兴媒体领域的佼佼者&#xff0c;凭借着生动&#xff0c;直观&#xff0c;代入感等元素的分享推荐收揽了巨额的流量。但是&#xff0c;随着时代的脚步逐渐加快&#xff0c;发展和变革随之涌来&#xff0c;传统的营销已经无法满足。所以场景营销就出现了。今天就来和…...

c++基础——数组

数组数组是存放相同类型对象的容器&#xff0c;数组中存放的对象没有名字&#xff0c;而是要通过其所在的位置访问。数组的大小是固定的&#xff0c;不能随意改变数组的长度。定义数组数组的声明形如 a[b]&#xff0c;其中&#xff0c;a 是数组的名字&#xff0c;b 是数组中元素…...

odoo15 登录界面的标题自定义

odoo15 登录界面的标题自定义 原代码中查询:<title>Odoo<title> <html> <head><meta http-equiv="content-type" content="text/html; charset=utf-8" /><title>Odoo</title><link rel="shortcut icon…...

【内网服务通过跳板机和公网通信】花生壳内网穿透+Nginx内网转发+mqtt服务搭建

问题&#xff1a;服务不能暴露公网 客户的主机不能连外网&#xff0c;服务MQTT服务部署在内网。记做&#xff1a;p1 (computer 1)堡垒机&#xff08;跳板机&#xff09;可以连外网&#xff0c;内网IP 和 MQTT服务在同一个网段。记做&#xff1a;p2 (computer 2)对他人而言&…...

【多线程常见面试题】

谈谈 volatile关键字的用法? volatile能够保证内存可见性,强制从主内存中读取数据,此时如果有其他线程修改被volatile修饰的变量,可以第一时间读取到最新的值 Java多线程是如何实现数据共享的? JVM把内存分成了这几个区域: 方法区,堆区,栈区,程序计数器&#xff1b; 其中堆区…...

深度剖析指针(下)——“C”

各位CSDN的uu们你们好呀&#xff0c;今天小雅兰的内容还是我们的指针呀&#xff0c;上两篇博客我们基本上已经把知识点过了一遍&#xff0c;这篇博客就让小雅兰来带大家看一些和指针有关的题目吧&#xff0c;现在&#xff0c;就让我们进入指针的世界吧 复习&#xff1a; 数组和…...

爬虫与反爬虫技术简介

互联网的大数据时代的来临&#xff0c;网络爬虫也成了互联网中一个重要行业&#xff0c;它是一种自动获取网页数据信息的爬虫程序&#xff0c;是网站搜索引擎的重要组成部分。通过爬虫&#xff0c;可以获取自己想要的相关数据信息&#xff0c;让爬虫协助自己的工作&#xff0c;…...

Pag的2D渲染执行流程

Pag的渲染 背景 根据Pag文章里面说的&#xff0c;Pag之前长时间使用的Skia库作为底层渲染引擎。但由于Skia库体积过大&#xff0c;为了保证通用型&#xff08;比如兼容CPU渲染&#xff09;做了很多额外的事情。所以Pag的工程师们自己实现了一套2D图形框架替换掉Skia&#xff…...

k8s 概念说明,k8s面试题

什么是Kubernetes&#xff1f; Kubernetes是一种开源容器编排系统&#xff0c;可自动化应用程序的部署、扩展和管理。 Kubernetes 中的 Master 组件有哪些&#xff1f; Kubernetes 中的 Master 组件包括 API Server、etcd、Scheduler 和 Controller Manager。 Kubernetes 中的…...

Docker--(四)--搭建私有仓库(registry、harbor)

私有仓库----registry官方提供registry仓库管理&#xff08;推送、删除、下载&#xff09;私有仓库----harbor私有镜像仓库1.私有仓库----registry官方提供 Docker hub官方已提供容器镜像registry,用于搭建私有仓库 1.1 镜像拉取、运行、查看信息、测试 (一) 拉取镜像 # dock…...

Invalid <url-pattern> [sso.action] in filter mapping

Tomcat 8.5.86版本启动web项目报错Caused by: java.lang.IllegalArgumentException: Invalid <url-pattern> [sso.action] in filter mapping 查看项目的web.xml文件相关片段 <filter-mapping><filter-name>SSOFilter</filter-name><url-pattern&g…...

【11】linux命令每日分享——useradd添加用户

大家好&#xff0c;这里是sdust-vrlab&#xff0c;Linux是一种免费使用和自由传播的类UNIX操作系统&#xff0c;Linux的基本思想有两点&#xff1a;一切都是文件&#xff1b;每个文件都有确定的用途&#xff1b;linux涉及到IT行业的方方面面&#xff0c;在我们日常的学习中&…...

Newman+Jenkins实现接口自动化测试

一、是什么Newman Newman就是纽曼手机这个经典牌子&#xff0c;哈哈&#xff0c;开玩笑啦。。。别当真&#xff0c;简单地说Newman就是命令行版的Postman&#xff0c;查看官网地址。 Newman可以使用Postman导出的collection文件直接在命令行运行&#xff0c;把Postman界面化运…...

MySQL:事务+@Transactional注解

事务 本章从了解为什么需要事务到讲述事务的四大特性和概念&#xff0c;最后讲述MySQL中的事务使用语法以及一些需要注意的性质。 再额外讲述一点Springboot中Transactional注解的使用。 1.为什么需要事务&#xff1f; 我们以用户转账为例&#xff0c;假设用户A和用户B的银行账…...

苏州企业做网站/青岛网站排名提升

学习时&#xff0c;为了搜集最全的中文资料&#xff0c;有时候不得不使用Baidu搜索引擎。在你还是个小菜鸡的时候你可能会花费大量时间在百度上&#xff01; 但是&#xff0c;时间久了你会发现&#xff0c;你总会被网络上一些奇奇怪怪或者有趣的事情吸引过去而逐渐忘记自己曾经…...

自由贸易试验区网站建设方案/短视频获客系统

1、HOG特征&#xff1a; 方向梯度直方图&#xff08;Histogram of Oriented Gradient, HOG&#xff09;特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子。它通过计算和统计图像局部区域的梯度方向直方图来构成特征。Hog特征结合SVM分类器已经被广泛应用于图像…...

政府单位网站建设方案/关键词搜索广告

java8的stream流学习(2) ***前言*** 之前也写过两三篇关于Stream相关的帖子,当然了,也是参考的.我感觉java8的新特性还是要深刻掌握的,因为这几个新特性的确能帮助我们让代码变得健壮,不说了,直接写案例,撸代码 参考网址: https://mp.weixin.qq.com/s/IHkpqdRLeEPAgdPbOnxsKw …...

怎么查那些人输入做网站/seo技术培训江门

神经网络的梯度下降&#xff08;Gradient descent for neural networks&#xff09; 假设单隐层神经网络会有W[1]W^{[1]}W[1]&#xff0c;b[1]b^{[1]}b[1]&#xff0c;W[2]W^{[2]}W[2]&#xff0c;b[2]b^{[2]}b[2]这些参数&#xff0c;还有个nxn_xnx​表示输入特征的个数&…...

建设标准 免费下载网站/灰色词优化培训

在 2003 系统 IIS6 下&#xff0c;经常出现 w3wp.exe 的内存占用不能及时释放和 CPU 占用居高不下的问题&#xff0c;从而导致服务器响应速度很慢。怎样才能找出是哪一个站点出问题呢&#xff1f; 首先给每个站点都建一个应用程序池&#xff0c;这样便于找出问题出在哪一个站点…...

如皋住房和城乡建设局网站/高端网站定制设计

2019独角兽企业重金招聘Python工程师标准>>> 使用react时,如果我们将层级划分的很清楚,那么就会经常出现子级组件更改父级组件状态的情况,而这种情况并不能单单由redux来解决,那么要怎么样方便快捷的实现这种需求呢? 其实说来也很容易, 简单地说就是在父级元素调用…...