KMP(Knuth-Morris-Pratt)算法,详细版

2024-03-22 11:10

本文主要是介绍KMP(Knuth-Morris-Pratt)算法,详细版,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

KMP(Knuth-Morris-Pratt)算法

1、KMP算法

在目标串T中搜索模式串P

​ 由于强制算法(暴力搜素算法)在找到不匹配的字符时,只能把模式串P(相对于目标串T)移动一个位置,导致大量的操作重复,冗余的次数随目标串,或模式串长度的增长,使得代码的执行效率直线下降。

​ 为了使搜素的效率提高,且不遗漏每一处匹配,KMP算法通过利用匹配失败处的位置信息,进行改进,使失败的匹配结果也能得到利用,加快字符串匹配效率

2、过程

2.1、给定目标串T,和模式串P

注意:一个字符串的前缀可以包括其本身

   0 1 2 3 4 5 6 7 8 9 10 11
T: a b c a b d a b a b c d
P: a b a b c

2.2、对模式串P创建前缀表PrefixTable

  模式串P前缀    最长公共前后缀       最长公共前后缀长度  index       prefix[index]
----------------------------------------------------------------------------------------
-1|          |      -          |        -        |  0	 |       -1      |
0 |	a        |     null        |        0        | 	1	 |        0      |
0 |	a b      |     null        |        0        |  2	 |        0      |
1 |	a b a    |      a          |        1        |  3	 |        1      |
2 |	a b a b  |      ab         |        2        |  4	 |        2      |
- |	a b a b c|     null        |        0        |  -	 |         -     |这一行不要

2.2.1具体操作:

  1. 写出模式串的前缀(由于包含本身)
  2. 根据模式串的前缀,写出对应的最长公共前后缀长度[0 0 1 2 0]
  3. [0 0 1 2 0],去掉最后一个元素,整体向右移动,在最前面写**-1**-------->[-1 0 0 1 2 ]

2.2.2得到前缀表

P: a  b  a  b  c-1  0  0  1  2  <---a下面应该是0,为了代码写起来方便写成-1

2.3 KMP进行过程

2.3.1、

外链图片转存失败(img-0hJ5lPEz-1567419238965)(![C:\Users\张腾森\Desktop\算法\j1.png)]

​ 1.当模式串P和目标串T匹配到第4个字符时,T[3] != p[3],这时注意到P串下面的与P串相对应的前缀表内容,取出prefix[3] == 1,然后把P串的p[ prefix[3] == 1 ]去和T[3]做比较。

​ 总结1-1:当模式串P[j]和目标串T[i]不匹配时**(i任意取的,完全是模式串在目标串上移动的,所以p串可能匹配t串的任意位置)**,获取P串对应的下标j,在用相同的下标j去获取前缀表中的内容int k = prefix[j]
然后用p[k]去和T[i]匹配,

[外链图片转存失败(img-Bi6PM7OZ-1567419238968)(C:\Users\张腾森\Desktop\算法\j2.png)]

​ 2.此时会发现,p[1]==b,t[3]==1;p[1]!=t[3],不匹配,于是继续上面的操作,在模式串P中取出不匹配处的下标·j==0,在通过该下标去获取前缀表的内容prefix[j==0]==0,再用p[0]去和t[3]做匹配

[外链图片转存失败(img-gbgHsZS8-1567419238970)(C:\Users\张腾森\Desktop\算法\j3.png)]

​ 3、发现p[0]==T[3],这时,移动目标串T的位置t[3---->4]p[0---->1],于是用T[4]==c,与p[0]==b进行匹配,发现T[4]!=p[1],重复第二步,获取模式串P在不匹配处的下标j==1,在通过该下标j==1找到前缀表中对应的值prefix[j==1]==0,于是用模式串j==0节点P[j==0]去对应t[4],

​ 总结1-2:当模式串P和目标串T同时匹配上时,目标串T和模式串P移动到下一个节点(i++,j++)进行匹配,如果不匹配,则重复总结1-1

4.注意到,p[1]==b;t[4]==c;是不匹配的,于是去前缀表prefix[1]==0;于是用p[0]==a去和t[4]==c去匹配,结果还是不匹配,**此时在去前缀
表取值发现prefix[0]== -1,这时候要做的就是用P[-1]==?去和t[4]==c做匹配,换就话说,就是把整个p串右移一个位置,和t的下一个元
素进行匹配(i++,j++)**

[外链图片转存失败(img-1kZPeIfU-1567419238973)(C:\Users\张腾森\Desktop\算法\j5.png)]

​ 5,继续匹配后,发现已经找到了第一个匹配成功的位置,此时返回在目标串中的当前索引位置i-模式串的长度

即第一个匹配成功的位置,

[外链图片转存失败(img-4mhtA7nn-1567419238974)(C:\Users\张腾森\Desktop\算法\j8.png)]

​ 6.当匹配成功时p的当前索引是4,于是取前缀表prefix[4]==2,再用p[2]==a,去和T串匹配,结果发现,到最和一个元素了(有可能出界),匹配结束。

[外链图片转存失败(img-qrPPNass-1567419238974)(C:\Users\张腾森\Desktop\算法\j9.png)]

3、代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void prefix_table(char pattern[], int prefix[], int n) {prefix[0] = 0;int len = 0;int	i =1;	while(i<n){if ( pattern[i] == pattern[len] ) {len++ ;prefix[i] = len;i++ ;}else {if(len > 0){len = prefix[ len-1];}else {prefix[i] = len;i++ ;}}}
}
void move_prefix_table(int prefix[], int n) {int i;for(i=n-1;i>0;i--){prefix[i] = prefix[i-1];}prefix[0] = -1;
}
void kmp_search( char text[], char pattern[]) {int n = strlen( pattern);//<----string.hint m = strlen( text );int* prefix =(int*)malloc( n*sizeof(int));//<--stdlib.h//int prefix[n]; prefix_table(pattern, prefix, n);move_prefix_table(prefix, n);
// text[i], Len( text)=m
// pattern[j] ,Len(pattern) = nint i=0;int j=0; while(i<m){if (j==n-1&&text[i]==pattern[j]) {printf("Found pattern at %d\n", i - j);j=prefix[j]; }if (text[i] == pattern[j]) {i++; j++;}else {j= prefix[j];if (j == -1) {i++; j++ ;}}}
} int main() {char pattern[] = "ABABCABAA";char text[]= "ABABABCABAABABABAB";kmp_search(text,pattern);return 0;
}

4、结果

[外链图片转存失败(img-shaFlpm4-1567419238980)(C:\Users\张腾森\Desktop\算法\j10.png)]

这篇关于KMP(Knuth-Morris-Pratt)算法,详细版的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/835182

相关文章

CSS中的Static、Relative、Absolute、Fixed、Sticky的应用与详细对比

《CSS中的Static、Relative、Absolute、Fixed、Sticky的应用与详细对比》CSS中的position属性用于控制元素的定位方式,不同的定位方式会影响元素在页面中的布... css 中的 position 属性用于控制元素的定位方式,不同的定位方式会影响元素在页面中的布局和层叠关

在Windows上使用qemu安装ubuntu24.04服务器的详细指南

《在Windows上使用qemu安装ubuntu24.04服务器的详细指南》本文介绍了在Windows上使用QEMU安装Ubuntu24.04的全流程:安装QEMU、准备ISO镜像、创建虚拟磁盘、配置... 目录1. 安装QEMU环境2. 准备Ubuntu 24.04镜像3. 启动QEMU安装Ubuntu4

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4

SQL Server数据库死锁处理超详细攻略

《SQLServer数据库死锁处理超详细攻略》SQLServer作为主流数据库管理系统,在高并发场景下可能面临死锁问题,影响系统性能和稳定性,这篇文章主要给大家介绍了关于SQLServer数据库死... 目录一、引言二、查询 Sqlserver 中造成死锁的 SPID三、用内置函数查询执行信息1. sp_w

Python UV安装、升级、卸载详细步骤记录

《PythonUV安装、升级、卸载详细步骤记录》:本文主要介绍PythonUV安装、升级、卸载的详细步骤,uv是Astral推出的下一代Python包与项目管理器,主打单一可执行文件、极致性能... 目录安装检查升级设置自动补全卸载UV 命令总结 官方文档详见:https://docs.astral.sh/

Python包管理工具核心指令uvx举例详细解析

《Python包管理工具核心指令uvx举例详细解析》:本文主要介绍Python包管理工具核心指令uvx的相关资料,uvx是uv工具链中用于临时运行Python命令行工具的高效执行器,依托Rust实... 目录一、uvx 的定位与核心功能二、uvx 的典型应用场景三、uvx 与传统工具对比四、uvx 的技术实

canal实现mysql数据同步的详细过程

《canal实现mysql数据同步的详细过程》:本文主要介绍canal实现mysql数据同步的详细过程,本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的... 目录1、canal下载2、mysql同步用户创建和授权3、canal admin安装和启动4、canal

SpringBoot集成LiteFlow实现轻量级工作流引擎的详细过程

《SpringBoot集成LiteFlow实现轻量级工作流引擎的详细过程》LiteFlow是一款专注于逻辑驱动流程编排的轻量级框架,它以组件化方式快速构建和执行业务流程,有效解耦复杂业务逻辑,下面给大... 目录一、基础概念1.1 组件(Component)1.2 规则(Rule)1.3 上下文(Conte

Springboot3+将ID转为JSON字符串的详细配置方案

《Springboot3+将ID转为JSON字符串的详细配置方案》:本文主要介绍纯后端实现Long/BigIntegerID转为JSON字符串的详细配置方案,s基于SpringBoot3+和Spr... 目录1. 添加依赖2. 全局 Jackson 配置3. 精准控制(可选)4. OpenAPI (Spri

MySQL复杂SQL之多表联查/子查询详细介绍(最新整理)

《MySQL复杂SQL之多表联查/子查询详细介绍(最新整理)》掌握多表联查(INNERJOIN,LEFTJOIN,RIGHTJOIN,FULLJOIN)和子查询(标量、列、行、表子查询、相关/非相关、... 目录第一部分:多表联查 (JOIN Operations)1. 连接的类型 (JOIN Types)