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

KMP算法的实现

         这是C++算法基础-数据结构专栏的第二十六篇文章,专栏详情请见此处


引入

        KMP算法是一种可以快速查找某一字符串在一个文本中的所有出现的算法。

        下面我们就来讲KMP算法的实现。

定义

        Knuth–Morris–Pratt 算法,简称KMP算法,是由Knuth、Pratt和Morris在1977年共同发布的一个算法,它是前缀函数的一个典型应用,应用是找到并展示一个字符串在中一个文本的所有出现。

过程

        前缀函数

        什么是前缀函数?要想了解这个函数,我们需要了解一下一个字符串的前缀,真前缀,后缀和真后缀这四个概念。

        前缀是指从串首开始到某个位置结束的一个特殊子串,而真前缀就是指除了这个字符串本身以外的前缀;后缀是指从某个位置开始到串尾结束的一个特殊子串,而真后缀就是指除了这个字符串本身以外的后缀。

        了解了以上的概念,我们再来看前缀函数的定义

        给定一个长度为n的字符串s,它的前缀函数被定义为一个长度为n的数组ne[]。其中ne[i]表示了子串s[1\cdots i]拥有的最长的一对相等的真前缀(s[1\cdots k])与真后缀(s[i-k\cdots i])的长度k(若没有,即为0)。

        KMP算法就是前缀函数的经典应用。接下来,我会展示一道KMP算法所实现的一道经典问题,从而讲解算法的思路。

        例题

        题目大意:给定一个文本和一个文本,找到并展示这个字符串在这个文本的所有出现时的下标。

        刚看到题面,可能大家只会打暴力,思路就是:从头开始比对字符串。若比对成功,则展示下标;若在比对时出现不同的字符,则停止比对,再从下一个位置继续比对。重复以上操作,直到遍历完成。

        聪明的小伙伴可能看出,比对的过程不能再优化了,进行优化的只能是比对失败后对于字符串的下一个比对位置,换句话说,比对失败后,我们不再从下一个位置继续比对,而是可以往后移动几位再比对,从而减少不必要的操作。

        而这个思路,就是KMP算法的核心思路。

        KMP算法主体

        对于正在比对的字符串str,它是在文本下标为i的位置开始比对的,并在文本下标为j的位置出现不同的字符,比对失败的。假设文本[i\cdots j-1]拥有一对相等的真前缀(s1)与真后缀(s2),又因为文本的真前缀有相对应的字符串的部分(s3),所以s1=s2=s3。又因为s2=s3,所以s3的是可以在s2的位置进行下一次比对的。这就是KMP算法的实现原理。

        很明显,上述操作实际上就是应用了前缀函数,为了方便快捷,我们可以先求出字符串(在上一段,我说的是文本的一对相等的真前缀与真后缀,其实文本[i\cdots j-1]是有字符串对应的)的前缀数组,再进行以上操作。

        还有最后一个要点,前缀函数求的是字符串拥有的最长的一对相等的真前缀与真后缀的长度,实际上,看似是最长,实际上最长的真后缀的起始位置最往前,但这样就避免了中间有遗漏的比对位置。

        在核心代码中,我使用了双指针算法,若文本长度为n,字符串长度为m,则时间复杂度为O(n+m)。如果想了解双指针算法的具体内容,可以移步至我的这篇博客:双指针算法的实现。

        一句话送给大家:

        一个人能走的多远不在于他在顺境时能走的多快,而在于他在逆境时多久能找到曾经的自己。

——KMP        

        嗯,这句话不是我说的,是我在网上看到的,但是它很好地概括了KMP算法的核心,也很有哲理。

性质

        时间复杂度

        O(n+m),在KMP算法主体中已经分析,故不再讲解。

代码

        下面给出KMP算法的实现代码:

string s,p;
int n,m;

for(int i=2,j=0;i<=m;i++){
    while(j&&p[i]!=p[j+1])
		j=ne[j];
    if(p[i]==p[j+1])
		j++;
    ne[i]=j;
}

for(int i=1,j=0;i<=n;i++){
    while(j&&s[i]!=p[j+1])
		j=ne[j];
    if(s[i]==p[j+1])
		j++;
    if(j==m){
        j=ne[j];
        
    }
}
        代码解释

        第一行的sp分别代表文本和字符串,第二行的nm分别代表sp的长度,第一个for循环是前缀函数的实现,第二个for循环是KMP算法主体的实现


上一篇-单调队列的实现    C++算法基础专栏文章    下一篇-​​​​​​​Trie树之字符串统计问题


每周六更新一篇文章,内容一般是自己总结的经验或是在其他网站上整理的优质内容

点个赞,关注一下呗~


http://www.kler.cn/a/313246.html

相关文章:

  • DAY6 线程
  • 后端:Aop 面向切面编程
  • Node.Js+Knex+MySQL增删改查的简单示例(Typescript)
  • 智能零售柜商品识别
  • 2024年11月10日系统架构设计师考试题目回顾
  • 光流法与直接法在SLAM中的应用
  • How can I integrate OpenAI Whisper model into a Kotlin app?
  • 解决 iOS App Tracking Transparency 权限问题
  • windows下,用docker部署xinference,为什么老是提示localhost无法访问?
  • 图说几何学2300年重大错误:附着在直线z上的直线段必是z的一部分
  • WebServer:buffer
  • Intel架构的基本知识
  • 我整理了一些关于【软考备考】的学习资料和大家一起分享、学习一下
  • 《 C++ 修炼全景指南:九 》打破编程瓶颈!掌握二叉搜索树的高效实现与技巧
  • STM32使用 :串口的接收与发送
  • 【RabbitMQ】RabbitMQ 概述
  • 在HTML中添加段落方法
  • 领域驱动DDD三种架构-分层架构、洋葱架构、六边形架构
  • ARM驱动学习之22 字符类GPIO
  • 智慧安防监控EasyCVR视频汇聚管理平台如何修改视频流分辨率?
  • 传输大咖44 | 云计算企业大数据迁移如何更安全高效?
  • docker-compose elasticsearch 集群搭建(用户登录+https访问)
  • Python 从入门到实战24(类的继承)
  • SCDN是服务器吗?SCDN防御服务器有什么特点?
  • 操作数据库
  • web前端-HTML常用标签-综合案例