【算法篇】KMP算法,一种高效的字符串匹配算法

news/2024/11/20 0:48:09/文章来源:https://www.cnblogs.com/bigbigli/p/18290597

我们今天了解一个字符串匹配算法-KMP算法,内容难度相对来说较高,建议先收藏再细品!!!

KMP算法的基本概念

KMP算法是一种高效的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。

该算法的主要使用场景就是在字符串(也叫主串)中的模式串(也叫字串)定位问题,常见的有“求子串出现的起始位置”、“求子串的出现次数”等。

解决什么问题

假设有两个字符串,分别为文本串和模式串,如下:


求在文本串中是否出现过上面的模式串。

暴力解法

当出现不匹配的字符时,暴力算法会进行如下两个操作:

  • 向后移动模式串
  • 目标串和模式串的指针都回溯

KMP优化解法

使用暴力算法的时间复杂度较高,如何去优化呢?

优化方向:防止或减少主串指针回溯

当出现不匹配的字符时,目标串指针不动,只移动模式串。

移动前,指针左边的字符已经匹配了,所以要让移动后的目标串的指针不会苏,需要保证:模式串移动之后,在指针左边的字符也是匹配的。

  • 找相同字符必须是从模式串第一个位置开始
  • 模式串移动方式由能找到的最长的相同字符决定,如果不是最长的,可能会漏掉能匹配的内容。
  • 找到的最长的相同字符串长度必须小于已经匹配的内容长度,前后部分可以有交叉内容

KMP算法小结

  • 发生不匹配时,指针所指的下标等于已经匹配的长度
  • 发生不匹配时,需要移动的长度 = 已经匹配的长度 - 前后相同的最大长度
  • 前后相同的最大长度为空的地方用-1补齐

KMP算法中的next数组

当目前的C和A不匹配时,由于A的前面也全都是A,所以前面也一定不匹配,对于这个模式串,可以直接将指针移动到-1的位置。

所以需要再对next数组进行改进,改进后的数组我们命名为nextval。

优化next数组

总结:若str[j] == str[next[j]],那么nextval[j] = nextval[next],否则nextval[j] = next[j]

判断是否匹配

先给定两个字符串,分别表示文本串和模式串,通过kmp(稍后写这个函数)进行比较,找到第一次出现模式串的位置,如果没有匹配上则给出提示。

char *text = "aaaaaabaaa",*pattern = "aaaab";
int index = kmp(text,pattern);
if(index == -1)
{cout << "没有匹配上内容";
} 
else{cout << "匹配上了,起始位置为:" << index;
}

输出next数组

next指针用来动态获取模式串的长度

int kmp(char *text,char *pattern){int index = -1;int txt_len = strlen(text),ptn_len = strlen(pattern);int *next = (int *)malloc(sizeof(int) * ptn_len);get_next(pattern,next,ptn_len);free(next);return index;
}

计算next数组

若str[j] == str[k]时,next[j+1] = k+1
若str[j] != str[k]时,k = next[k]

void get_next(char *str,int *next,int len){int j = 0,k = -1;next[0] = -1;while(j < len-1){if(k == -1 || str[j] == str[k]){k++;j++;next[j] = k;}else k = next[k];} 
}

遍历输出next数组

从下标为0的位置到ptn_len依次输出next数组内的元素

int kmp(char *text,char *pattern)
{int index = -1;int txt_len = strlen(text),ptn_len = strlen(pattern);int *next = (int *)malloc(sizeof(int) * ptn_len);get_next(pattern,next,ptn_len);for(int i=0;i<ptn_len;i++){printf("%d ",next[i]);}free(next);return index;
}

输出nextval数组

将next数组变为nextval数组(此处的next数组实际上是nextval数组)

if(k == -1 || str[j] == str[k]){k++;j++;if(str[j] == str[k]){next[j] = next[k];}else{next[j] = k;}
}
else{k = next[k];
} 

输出匹配位置

int index = -1,txt_idx = 0,ptn_idx = 0;
... ...
get_next(pattern,next,ptn_len);while((txt_idx < txt_len) && (ptn_idx < ptn_len))
{if(text[txt_idx] == pattern[ptn_idx] || ptn_idx == -1){txt_idx++;ptn_idx++;}else{ptn_idx = next[ptn_idx];}
}if(ptn_idx >= ptn_len){index = txt_idx - ptn_len;
}

利用KMP算法解决字符串匹配问题,能极大节约时间复杂度。关于KMP算法还有什么问题的话,欢迎各位留言交流~

本文由mdnice多平台发布

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

飞书集成平台,流程触发器触发,没有运行日志

原因:飞书平台自动关联自建应用时,回调地址生成存在问题。解决办法:将飞书的触发器回调地址的内容,复制到关联的自建应用的事件回调地址中

NOIP2024模拟1

NOIP2024模拟1\(T1\) GHzoj 3752. 分糖果 \(100pts\)设最终答案中有 \(a\) 个小组中的小朋友的糖数 \(\mod 3\) 均等于 \(1\) , \(b\) 个小组中的小朋友的糖数 \(\mod 3\) 互不相等, \(c\) 个小组中的小朋友的糖数 \(\mod 3\) 均等于 \(0\) , \(d\) 个小组中的小朋友的糖数 …

工具安装

虚拟机——VMware安装 Vmware官方地址kali镜像 kali镜像win10镜像(教育版) 工具站卸载工具——geek安装文件检索工具——everything everything下载地址截图工具——snipaste snipaste下载地址解压缩工具——7.zip 7.zip下载地址 文本编辑器——notepad++ notepad++下载地址 …

分拆、杨图和杨表

分拆、杨图和杨表\(1.\) 定义和性质\(\textbf{定义 1 } \text{(分拆)}\) 定义整数 \(n\) 的一个拆分为 \(\lambda = (\lambda_1, \lambda_2, \dots, \lambda_m)\),满足 \(\forall i, \lambda_i \ge \lambda_{i + 1}\),且 \(\lambda_i\) 的加和为 \(n\)。记其为 \(\lambda \vd…

常见的概率分布

1.离散型分布 1.1 两点分布(伯努利分布/贝努利分布/0-1分布) 称随机变量 \(X\) 服从参数为 \(p\) 的伯努利分布,如果它分别以概率 \(p\) 和 \(1-p\) 取 1 和 0 为值。​ \[P(X=k)=p^k(1-p)^{1-k}, \quad k=0,1\\ X\sim B(1,p)\\ E(X)=p\\ D(X)=p(1-p) \]1.2 二项分布 n次独立…

python使用flask框架生成excle返回前端(包含图片、表格、表头灰色、表格加边框)

python使用flask框架生成excle文档,文档中包含图片和表格,其中表格要包含图片、表格、表头灰色、表格加边框,照片和表格不重叠。 逻辑:获得图片的高度,根据高度计算表格从第几行开始插入。 效果图:代码:import openpyxl from openpyxl.styles import PatternFill from o…

生成扩散模型漫谈(二):DDPM = 自回归式VAE

在文章《生成扩散模型漫谈(一):DDPM = 拆楼 + 建楼》中,我们为生成扩散模型DDPM构建了“拆楼-建楼”的通俗类比,并且借助该类比完整地推导了生成扩散模型DDPM的理论形式。在该文章中,我们还指出DDPM本质上已经不是传统的扩散模型了,它更多的是一个变分自编码器VAE,实际…

生成扩散模型漫谈(三):DDPM = 贝叶斯 + 去噪

到目前为止,笔者给出了生成扩散模型DDPM的两种推导,分别是《生成扩散模型漫谈(一):DDPM = 拆楼 + 建楼》中的通俗类比方案和《生成扩散模型漫谈(二):DDPM = 自回归式VAE》中的变分自编码器方案。两种方案可谓各有特点,前者更为直白易懂,但无法做更多的理论延伸和定量…

ComfyUI进阶篇:ComfyUI核心节点(四)

ComfyUI核心节点(四) 前言: 学习ComfyUI是一场持久战。当你掌握了ComfyUI的安装和运行之后,会发现大量五花八门的节点。面对各种各样的工作流和复杂的节点种类,可能会让人感到不知所措。在这篇文章中,我们将用通俗易懂的语言对ComfyUI的核心节点进行系统梳理,并详细解释…

全网最全EdgeMesh QA手册

https://zhuanlan.zhihu.com/p/585749690全网最全EdgeMesh Q&A手册Poorunga今天摸大鱼 24 人赞同了该文章​目录收起转载请注明出处前言定位模型问题一:Failed to watch xxx: failed to list xxx: no kind xxx ; Reflector ListAndWatch xxx (total time 10003ms)问题二:…

.NetCore中EFCore for MySql整理MySql.EntityFrameworkCore

一、MySql.EntityFrameworkCore 这个是官方给的一个EF操作MySql数据库的框架。 使用方法跟EF for SqlServer 一样。二、安装命令NuGet\Install-Package MySql.EntityFrameworkCore -Version 8.0.5 项目依赖 安装后的结果: 三、 EF Code First 模式连接数据库更多: C#程序调…

Caterpillar on a Tree

首先一个很显然的地方就是使用传送门肯定是在叶子节点使用,我们来考虑一下整个过程是怎么样的 为了方便,我们不妨假设可以传送回根节点\(k+1\)次,然后要求最后回到根节点 我们先从根节点走到某一个叶子结点,然后再从这个叶子节点走到另一个叶子节点,然后继续走到另一个叶子…