【优选算法】KMP模式匹配算法 {算法介绍;算法原理:核心原理,如何求next数组;代码实现}

server/2024/11/25 10:39:39/

一、算法介绍

KMP算法,全称Knuth-Morris-Pratt算法,是一种线性时间复杂度的字符串匹配算法。该算法由D.E.Knuth、J.H.Morris和V.R.Pratt提出,因此也称为克努特—莫里斯—普拉特操作。它主要用于在一个较长的字符串(称为主串或目标串)中查找一个较短的字符串(称为子串或模式串)的位置。

核心思想

KMP算法的核心思想是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数,以达到快速匹配的目的。

与朴素的字符串匹配算法(即每次匹配失败时,主串和模式串的指针都会回溯)不同,KMP算法在匹配失败时,主串的指针不回溯,仅通过移动模式串的指针,并借助一个预处理得到的数组(称为next数组或部分匹配表)来决定下一个匹配的位置,从而提高了匹配效率。

next数组

  • 定义:next数组是一个与模式串等长的数组,用于存储模式串中每个位置之前的子串的最长相同前后缀长度。具体地,对于模式串中的每个位置i(从1开始计数),next[i]表示以i结尾的前缀子串的最长相同前后缀长度。
  • 构建方法:遍历模式串,对于每个位置i,计算以i结尾的前缀子串的最长相同前后缀长度。这通常通过比较前缀子串的后缀和之前已经计算过的前缀子串的前缀来实现。
  • 作用:在匹配过程中,当模式串的某个位置与主串不匹配时,可以根据next数组直接跳转到模式串的下一个可能匹配的位置,而无需像朴素算法那样回溯主串和模式串的指针。

算法步骤

  1. 预处理:构建模式串的next数组。
  2. 匹配过程:
    • 初始化两个指针i和j,分别指向主串和模式串的起始位置。
    • 遍历主串,对于每个位置i:
      • 如果text[i]等于pattern[j],则i和j同时向右移动一格。
      • 如果text[i]不等于pattern[j],则根据next数组将j回退到下一个可能匹配的位置(即j=next[j-1],如果j>0;否则i向右移动一格,j保持为0)。
    • 如果j等于模式串的长度,则说明匹配成功,返回匹配起始位置(即i-j)。
    • 如果遍历完主串仍未找到匹配,则返回-1表示未找到。

时间复杂度和空间复杂度

  • 时间复杂度:KMP算法的时间复杂度为O(m+n),其中m是主串的长度,n是模式串的长度。这是因为KMP算法只需遍历主串和模式串各一次,并且在发生不匹配时能够利用next数组进行高效的跳转,从而避免了不必要的比较操作。(朴素模式匹配算法的时间复杂度:O(m*n))
  • 空间复杂度:KMP算法需要额外的空间来存储next数组,其空间复杂度为O(n)。

应用场景

KMP算法在实际应用中具有广泛的用途,特别是在文本搜索、生物信息学、网络爬虫等领域。例如,在文本编辑器中实现查找功能、在DNA序列分析中查找特定的基因序列等场景都可以使用KMP算法来提高效率。

综上所述,KMP算法是一种高效且实用的字符串匹配算法,它通过预处理模式串和构建next数组来优化匹配过程,减少了不必要的比较次数,从而提高了匹配效率。


二、算法原理

2.1 核心思想

【天勤考研】KMP算法易懂版_哔哩哔哩_bilibili

人类语言:当出现不匹配项时,直接移动将模式串已匹配子串的公共前缀移动到公共后缀的位置,目标串指针不回溯,继续向后比较。

注意:这里是最长公共前后缀,且长度小于已匹配子串的长度(必须是两个不同位置的串,否则没法移动)。

在这里插入图片描述

机器语言:当出现不匹配项时,假设匹配子串的公共前后缀的长度为n,就从模式串的n+1号位开始继续比较。

在这里插入图片描述

因此我们需要对模式串进行预处理,找到每个位置之前的公共前后缀的长度+1(移动后的比较位置),这就是next数组,next是下一个比较位置的意思。

在这里插入图片描述

使用next数组:

如果模式串的n号位不匹配,对应next数组的下标,模式串指针就跳转到next数组n号位标记的下标处与主串当前位继续比较

  1. 1号位不匹配,主串指针i++
  2. n号位不匹配(n>1),模式串指针j = next[j](如果下标从0开始不需要+1)

注意:这里的数组下标是从1开始的,在实际代码中,下标从0开始,next[j] = j位置之前的公共前后缀的长度(不需要+1)


2.2 如何求next数组?

KMP算法之求next数组代码讲解_哔哩哔哩_bilibili

  • 如果模式串下标从0开始,next[0]初始化为-1,next[j] = j位置之前的公共前后缀的长度;
  • 如果模式串下标从1开始,next[1]初始化为0,next[j] = j位置之前的公共前后缀的长度 + 1;

提示:next首位置的初始值并无实际意义,只是一个标记值,表示首位置与主串下一位比较。

  1. 首先要知道求next[j]要看的是j位置之前的最长公共前后缀,与j位置的值无关

  2. 类似于动态规划算法,我们可以利用之前位置的next值求当前位置

  3. 令k = next[j-1],如果str[k] == str[j-1],那么next[j] = k+1

    在这里插入图片描述

  4. 但是如果不匹配,就需要再找公共前后缀的公共前后缀了,比较最前缀和最后缀的末尾值

  5. 即令k = next[k],如果str[k] == str[j-1],那么next[j] = k+1

    在这里插入图片描述

  6. 如果还不匹配,就重复上述步骤继续递推,直到k==0(首位置next越界)则递推结束。此时的next[j] = 1(首位置),表示前j-1位的前后缀中没有一位是重合的是最不理想的情况,如果j位置不匹配则模式串回溯到1号位与主串当前位置比较。


三、代码实现

完整代码:mystring · 52Hertz_Echo/CPP_Practice - 码云 - 开源中国 (gitee.com)

MYstring::Find_KMP

  size_t Find_KMP(const Mystring &substr, size_t pos = 0) const{// 预处理模式串,得到对应的next数组int m = _size;int n = substr.size();int next[n];substr.GetNext(next);// Debug:打印next数组进行检查// for (auto e : next)//   cout << e << " ";// cout << endl;// 利用next数组进行KMP模式匹配int i = pos, j = 0;while (i < m && j < n){if (j == -1 || _str[i] == substr[j]) //首位置不匹配 || 当前位置匹配{++j;++i;}else{j = next[j];}}if (j == n)return i - n;elsereturn -1;}

Mystring::GetNext

  void GetNext(int next[]) const{next[0] = -1; int k = -1; // k = next[i-1]int i = 1;while (i < _size){if (k == -1 || _str[i - 1] == _str[k]){next[i] = k + 1;++k; // k = next[i-1]++i;}else{k = next[k];}}}

Mystring::Find_BF

  size_t Find_BF(const Mystring &substr, size_t pos = 0) const{size_t n = substr.size();for (size_t i = pos; i < _size; ++i){if (_str[i] == substr[0]){size_t j = 1;while(j < n && _str[i + j] == substr[j]){++j;}if (j == n){return i;}}}return -1;}

http://www.ppmy.cn/server/144771.html

相关文章

前端入门之VUE--基础与核心

前言 VUE是前端用的最多的框架&#xff1b;这篇文章是本人大一上学习前端的笔记&#xff1b;欢迎点赞 收藏 关注&#xff0c;本人将会持续更新。 Vue学习笔记 用于构建用户界面的渐进式框架 构建用户界面&#xff1a;基于数据动态渲染页面渐进式&#xff1a;循序渐近的学…

JavaScript 不常用方法总结(选中高亮/元素定位应用等)

文章目录 getBoundingClientRect实例应用contenteditablecontenteditable 属性介绍内部文本选中高亮实例&#xff0c;3秒后高亮消失 父元素定位特定子元素内容定位效果方式一&#xff1a;使用JavaScript&#xff0c;在页面加载完成后设置滚动位置&#xff08;适用于大多数现代浏…

利用Prompt工程为LLM提升推理能力

利用Prompt工程为LLM提升推理能力 基于策略的推理详解ReAct: 推理与行动思维链&#xff1a;逐步解决问题反思&#xff1a;深入分析和自我审查与代理架构的集成实际应用代码附录 众所周知&#xff0c;一个精心设计的Prompt能够显著增强大型语言模型&#xff08;LLMs&#xff09;…

1+X应急响应(网络)常见网络攻击-SQL注入:

常见网络攻击-SQL注入&#xff1a; SQL注入概述&#xff1a; 动态网站的工作流程&#xff1a; SQL注入的起源&#xff1a; SQL典型的攻击手段&#xff1a; SQL注入的危害&#xff1a; SQL注入的函数&#xff1a; SQL注入类型&#xff1a; 提交方式分类&#xff1a; Get注入&am…

docker搭建私有仓库,实现镜像的推送和拉取

1.拉取docker仓库镜像 docker pull registry 2.启动registry容器 docker run -d registry 3.查看当前仓库中存在的镜像&#xff08;一&#xff09; curl -XGET http://192.168.111.162: 5000/v2/_catalog 192.168.111.162 部署docker仓库宿主机的ip 5000 部署docker仓库映射到宿…

SQL注入--文件读写注入--理论

什么是文件读写注入&#xff1f; MySQL中有 读取文件的函数&#xff1a;load_file() 写入文件的函数&#xff1a;Into outfile&#xff08;能写入多行&#xff0c;按格式输出&#xff09;和 into dumpfile&#xff08;只能写入一行且没有输出格式&#xff09; 利用这些函数在S…

狂野飙车8+(Asphalt 8+) for Mac 赛车竞速游戏 安装教程

Mac分享吧 文章目录 狂野飙车8(Asphalt 8) for Mac 赛车竞速游戏软件 效果图展示一、狂野飙车8(Asphalt 8) 赛车竞速游戏 Mac电脑版——v2.1.11️⃣&#xff1a;下载软件2️⃣&#xff1a;安装软件2.1 左侧安装包拖入右侧文件夹中&#xff0c;等待安装完成&#xff0c;运行软件…

c# new

目录 1、运算符&#xff08;operator&#xff09;1.1 对象创建表达式1.2 数组创建表达式1.3 委托创建表达式 2、修饰符&#xff08;modifier&#xff09; 在c#中&#xff0c;new是关键字之一&#xff0c;new关键字主要有以下两个用途&#xff1a;运算符、修饰符。 1、运算符&a…