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

【数据结构与算法】Knuth-Morris-Pratt 算法(KMP算法):一种在字符串中查找子串的算法

引言

KMP(Knuth-Morris-Pratt)算法是一个在字符串中查找子串的算法,由 Donald Knuth、Vaughan Pratt 和 James H. Morris 共同发明。这个算法的特点是在查找过程中,不会回溯主串,也不会重复扫描已经比较过的子串,因此它的时间复杂度是线性的。它的主要优点是在比对过程中,当一个字符不匹配时,可以跳过一些无需再次比对的字符,从而提高匹配效率。


相关概念

模式串(Pattern String)

在字符串搜索算法中,"模式串"是你正在查找的特定字符串。例如,如果你在一本书中查找单词 “apple”,那么 “apple” 就是你的模式串。

主串(Main String)

相对的,"主串"是你在其中进行查找的大段文本。在上面的例子中,整本书的文本就是主串。

前缀(Prefix)

对于字符串str,其长度为k的前缀是指从第一个字符开始的长度为k的子串。

后缀(Suffix)

对于字符串str,其长度为k的后缀是指从最后一个字符开始的长度为k的子串。

部分匹配表(Partial Match Table)

部分匹配表(Partial Match Table,也称为next数组或者失败函数)是KMP算法的核心组成部分,它是对模式串进行预处理得到的一个数组。这个表用于记录模式串中每个位置之前的子串的最长公共前后缀的长度。


基本思想

KMP算法的主要思想是利用已经部分匹配的有效信息,使得后续的匹配可以跳过那些已知肯定不会匹配的字符。

KMP 算法的核心是一个叫做部分匹配表(Partial Match Table, PMT)或者失败函数的预处理过程。这个表格记录了子串的前缀集合与后缀集合的最长公共元素的长度。在查找过程中,如果发生字符不匹配,我们可以通过这个表格快速移动子串的位置,跳过一些不可能匹配的位置。

例如,对于字符串"ABCDABD",其部分匹配表如下:

字符ABCDABD
PMT0000120

这个表的意思是,在模式串中,位置i之前的子串的最长公共前后缀的长度是PMT[i]。例如,位置5之前的子串是"ABCDAB",它的最长公共前后缀是"AB",长度是2,所以PMT[5]=2。

当在主串中进行匹配时,如果遇到不匹配的字符,可以利用部分匹配表中的信息,将模式串向右滑动一定的距离,从而跳过一些肯定不会匹配的位置,提高匹配效率。

在计算部分匹配表时,通常会使用动态规划的思想,对每个位置,都检查它之前的所有子串,找出最长的那个公共前后缀。

这个部分匹配表在KMP算法中有一个非常重要的作用,那就是当模式串中的某个字符与主串不匹配时,可以根据这个表直接跳过一部分字符,而不需要一个一个地去比对。

通过这种方法,KMP 算法可以在 O(n+m) 的时间复杂度内完成字符串的查找任务,其中 n 是主串的长度,m 是子串的长度。


详细步骤

  1. 预处理阶段
    生成部分匹配表。对于每个位置 i,我们计算子串 s2[1...i] 的最长相等的真前缀和后缀的长度 pmt[i]。这个长度就是当 s2[i+1] 和主串的某个字符不匹配时,我们需要将子串移动到的位置。
	const int N = 1e7 + 7;string s1, s2;int pmt[N];cin >> s1 >> s2;s1 = " " + s1;s2 = " " + s2;int l1 = s1.length() - 1;int l2 = s2.length() - 1;int j;	// 当前已经匹配的字符数量// 生成部分匹配表j = 0;for (int i = 2; i <= l2; i++) {// 下一个字符不匹配while (j && s2[i] != s2[j + 1]) {// 向前回溯j = pmt[j];}// 下一个字符匹配if (s2[i] == s2[j + 1]) {// j 后移一位j++;}// 更新部分匹配表pmt[i] = j;}
  1. 查找阶段
    从左到右扫描主串,同时移动子串的位置。如果主串的某个字符和子串的当前字符不匹配,我们就通过部分匹配表移动子串的位置。具体的移动方法是,将子串的位置向右移动 i - pmt[i] 个位置,其中 i 是子串中最后一个与主串匹配的字符的位置。
	// 查找字符串j = 0;for (int i = 1; i <= l1; i++) {// 下一个字符不匹配while (j && s1[i] != s2[j + 1]) {// 向前回溯j = pmt[j];}// 下一个字符匹配if (s1[i] == s2[j + 1]) {// j 后移一位j++;}// 匹配到字符串if (j == l2) {cout << i - l2 + 1 << endl;// 向前回溯,继续查找j = pmt[j];}}

http://www.xdnf.cn/news/809875.html

相关文章:

  • 位图格式详解
  • sockaddr和sockaddr_in详解
  • 全国最搞笑的名字都在这了,看了不准笑!
  • matlab中imfilter函数的使用
  • 什么是第三方支付通道接口?
  • 《Linux驱动:USB设备驱动看这一篇就够了》
  • 网络爬虫爬取常见网站数据
  • 网络安全最新程序员副业接单做私活避坑指南
  • BAT机器学习面试1000题系列
  • Linux系统安装(超级详细教程)
  • 通过修改ajaxFileUpload.js实现多图片动态上传并实现预览
  • 终于有人把TCP协议与UDP协议给搞明白了
  • 2022考研计算机-数据库原理教程1-7章
  • Qt 的发展历史、现状与启示
  • 经纬度编码方法推荐-plus code简介
  • 搭建CA并签发数字证书
  • Cisco 路由过滤之 Route-map Distribute-list
  • 产生随机数的好方法random_shuffle()
  • 中国计量计算机组成原理,中国计量学院计算机组成原理课程设计
  • SecureCRT 常用命令
  • ASP.NET全部教程
  • 什么是UCML
  • 使用SchedulerFactoryBean集成Quarz Job与Spring
  • Web 2.0(维基百科)
  • Oracle之主键(Primary Key)用法详解
  • Android ActivityManagerService总结(一)AMS启动
  • 智慧档案室一体化建设方案
  • Linux makefile详解
  • .NET Framework 3.5 SP1 最终文件下载及离线安装
  • 一站式Shell编程攻略:从入门到精通