KMP算法——字符串匹配问题

2023-05-16

贴上原址:http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html

感觉这篇文章讲得很不错,很容易懂,比算法导论上好懂多了。。。

字符串匹配是计算机的基本任务之一。

举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串"ABCDABD"?

许多算法可以完成这个任务,Knuth-Morris-Pratt算法(简称KMP)是最常用的之一。它以三个发明者命名,起头的那个K就是著名科学家Donald Knuth。

这种算法不太容易理解,网上有很多解释,但读起来都很费劲。直到读到Jake Boxer的文章,我才真正理解这种算法。下面,我用自己的语言,试图写一篇比较好懂的KMP算法解释。

1.

首先,字符串"BBC ABCDAB ABCDABCDABDE"的第一个字符与搜索词"ABCDABD"的第一个字符,进行比较。因为B与A不匹配,所以搜索词后移一位。

2.

因为B与A不匹配,搜索词再往后移。

3.

就这样,直到字符串有一个字符,与搜索词的第一个字符相同为止。

4.

接着比较字符串和搜索词的下一个字符,还是相同。

5.

直到字符串有一个字符,与搜索词对应的字符不相同为止。

6.

这时,最自然的反应是,将搜索词整个后移一位,再从头逐个比较。这样做虽然可行,但是效率很差,因为你要把"搜索位置"移到已经比较过的位置,重比一遍。

7.

一个基本事实是,当空格与D不匹配时,你其实知道前面六个字符是"ABCDAB"。KMP算法的想法是,设法利用这个已知信息,不要把"搜索位置"移回已经比较过的位置,继续把它向后移,这样就提高了效率。

8.

怎么做到这一点呢?可以针对搜索词,算出一张《部分匹配表》(Partial Match Table)。这张表是如何产生的,后面再介绍,这里只要会用就可以了。

9.

已知空格与D不匹配时,前面六个字符"ABCDAB"是匹配的。查表可知,最后一个匹配字符B对应的"部分匹配值"为2,因此按照下面的公式算出向后移动的位数:

  移动位数 = 已匹配的字符数 - 对应的部分匹配值

因为 6 - 2 等于4,所以将搜索词向后移动4位。

10.

因为空格与C不匹配,搜索词还要继续往后移。这时,已匹配的字符数为2("AB"),对应的"部分匹配值"为0。所以,移动位数 = 2 - 0,结果为 2,于是将搜索词向后移2位。

11.

因为空格与A不匹配,继续后移一位。

12.

逐位比较,直到发现C与D不匹配。于是,移动位数 = 6 - 2,继续将搜索词向后移动4位。

13.

逐位比较,直到搜索词的最后一位,发现完全匹配,于是搜索完成。如果还要继续搜索(即找出全部匹配),移动位数 = 7 - 0,再将搜索词向后移动7位,这里就不再重复了。

14.

下面介绍《部分匹配表》是如何产生的。

首先,要了解两个概念:"前缀"和"后缀"。 "前缀"指除了最后一个字符以外,一个字符串的全部头部组合;"后缀"指除了第一个字符以外,一个字符串的全部尾部组合。

15.

"部分匹配值"就是"前缀"和"后缀"的最长的共有元素的长度。以"ABCDABD"为例,

  - "A"的前缀和后缀都为空集,共有元素的长度为0;

  - "AB"的前缀为[A],后缀为[B],共有元素的长度为0;

  - "ABC"的前缀为[A, AB],后缀为[BC, C],共有元素的长度0;

  - "ABCD"的前缀为[A, AB, ABC],后缀为[BCD, CD, D],共有元素的长度为0;

  - "ABCDA"的前缀为[A, AB, ABC, ABCD],后缀为[BCDA, CDA, DA, A],共有元素为"A",长度为1;

  - "ABCDAB"的前缀为[A, AB, ABC, ABCD, ABCDA],后缀为[BCDAB, CDAB, DAB, AB, B],共有元素为"AB",长度为2;

  - "ABCDABD"的前缀为[A, AB, ABC, ABCD, ABCDA, ABCDAB],后缀为[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的长度为0。

16.

"部分匹配"的实质是,有时候,字符串头部和尾部会有重复。比如,"ABCDAB"之中有两个"AB",那么它的"部分匹配值"就是2("AB"的长度)。搜索词移动的时候,第一个"AB"向后移动4位(字符串长度-部分匹配值),就可以来到第二个"AB"的位置。

最后copy上代码:

// Compute Prefix function            
void CptPfFunc( ElemType Pattern[], int PrefixFunc[] )                
{      
        register int iLen = 0;    // Length of Pattern[]            
        while( '\0' != Pattern[iLen] )            
                iLen++;            
                    
        int LOLP = 0;     // Lenth of longest prefix            
        PrefixFunc[1] = 0;            
         
        for( int NOCM=2; NOCM<iLen+1; NOCM++ )     // NOCM represent the number of characters matched            
        {            
                while( LOLP>0 && (Pattern[LOLP] != Pattern[NOCM-1]) )            
                        LOLP = PrefixFunc[LOLP];            
                if( Pattern[LOLP] == Pattern[NOCM-1] )            
                        LOLP++;            
                PrefixFunc[NOCM] = LOLP;            
        }            
}            
    
void KMPstrMatching( ElemType Target[], ElemType Pattern[] )            
{            
        int PrefixFunc[MAX_SIZE];            
        register int TarLen = 0;            
        register int PatLen = 0;            
         
        // Compute the length of array Target and Pattern            
        while( '\0' != Target[TarLen] )            
                TarLen++;            
         
        while( '\0' != Pattern[PatLen] )            
                PatLen++;            
                    
        // Compute the prefix function of Pattern            
        CptPfFunc( Pattern, PrefixFunc );            
         
        int NOCM = 0;     // Number of characters matched            
         
        for( int i=0; i<TarLen; i++ )            
        {            
                while( NOCM>0 && Pattern[NOCM] != Target[i] )            
                        NOCM = PrefixFunc[NOCM];            
                if( Pattern[NOCM] == Target[i] )            
                        NOCM++;            
                if( NOCM == PatLen )            
                {            
                        cout<<"KMP String Matching,pattern occurs with shift "<<i - PatLen + 1<<endl;            
                        NOCM = PrefixFunc[NOCM];            
                }            
        }            
}     

说实话我感觉代码挺难懂的。。。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

KMP算法——字符串匹配问题 的相关文章

  • 虚拟机中使用OpenGL遇到的错误总结

    由于VMware对OpenGL的支持有限 xff0c 目前最新版本的VMware workstation15 Pro只支持到OpenGL3 3的core profile xff08 核心模式 xff09 xff0c 在有条件的前提下建议安装
  • 视觉SLAM——视觉里程计解决方案分析(间接法)

    目录 基本问题 分析各类求解方案优缺点分析 基本问题 视觉里程计是视觉SLAM技术的起点 xff0c 其核心问题同SLAM技术一样 xff0c 主要是定位与构图 xff0c 但视觉里程计解决的核心是定位问题 xff0c 也就是相机的位姿 通
  • 视觉SLAM理论——位姿的理解与间接求解

    目录 xff1a 位姿的定义位姿与变换矩阵的区别与联系位姿的求解方法 位姿的定义 在SLAM中 xff0c 位姿是世界坐标系到相机坐标系的变换 xff0c 包括旋转与平移 根据以上定义可以衍生以下几个问题 xff1a 1 世界坐标系在哪 x
  • 线性最小均方误差算法(LMSE),最小二乘法(LS)

    目录 背景正交投影引理LMSE算法LS算法直线拟合 背景 对于一个系统 xff0c 在给予一定的输入 xff0c 那么通常都会产生相对应的输出 在实际的系统中 xff0c 这样的输出必然伴随着噪声 xff0c 这样被噪声污染的输出通常是传感
  • 无人机,动力系统建模

    建模目的 无人机动力系统包括 xff1a 螺旋桨 电机 电调及电池 建模流程图如下 xff08 图片来源 多旋翼飞行器设计与控制 M 全权 xff09 xff1a 经过误差结算后 xff0c 将误差信息转换为螺旋桨的升力与转矩 xff0c
  • 寻找APM中EKF的五大公式

    EKF核心代码位置 AP NavEKF2 cpp 进入该函数 进入该函数 xff0c 然后可以看到关键部分 xff0c 也即卡尔曼五个公式的地方 下面介绍每个公式的具体位置 28状态值 首先要知道选用的状态值有哪些 xff0c 28状态值
  • 【PX4 EKF simulink仿真程序解析】(一)初始化

    PX4 EKF simulink仿真程序解析 xff08 一 xff09 初始化 整体框架如下 xff1a 进入InertialNavFliter xff0c 整体框架如下 xff1a 初始化过程包括协方差初始化 状态向量初始化 其中包括测
  • C++——三种继承方式与三种访问权限的相互组合

    三种访问权限 public 可以被任意实体访问 protected 只允许子类及本类的成员函数访问 private 只允许本类的成员函数访问 三种继承方式 public 继承 protect 继承 private 继承 组合结果 基类中 继
  • 小米路由器部分机型刷原生Openwrt系统

    小米路由器的部分机型在官网没有开发版的固件 xff0c 不支持直接开启ssh xff0c 可以通过OpenWRTInvasion工具解决 本文以小米路由器4为例 xff1a 在openwrt官网的设备列表中找到对应型号 xff0c 按照页面
  • qt的安装与卸载

    通常情况下 xff0c 有两种安装方法 xff1a 1 直接在命令行安装 sudo apt span class hljs keyword get span install qt5 span class hljs keyword defau
  • 固件提取

    前言使用工具识别芯片一 摘取芯片二 制作U盘编程器三 RT809H编程器读取eMMC芯片数据四 总结 前言 无处不在的物联网设备 xff0c 也可能成为无所不在的安全隐患 xff0c 物联网安全问题一直是困扰物联网快速发展的一大难题 作为安
  • xshell评估过期解决办法,非常简单

    首先 xff0c 你的xshell不要卸载 xff0c 不需要动任何地方 进官网 https www netsarang com zh xff0c 翻到最下面 xff0c 下载那里点家庭 学校免费 然后会跳转到下面这个界面 xff0c 按图
  • vscode配置markdown,安装插件

    一 概述 最近迷上了MarkDown xff0c 所以进行了学习 xff0c 首先是编辑器的选择 xff0c 可以参考这篇文章 xff1a 好用的MARKDOWN编辑器一览 我本人并没有选择其中的任意一款进行尝试 xff0c 因为我个人十分
  • Vs2019重新生成解决方案时报错

    解决办法 xff1a Release模式下 gt 属性 gt 高级 gt 高级属性 gt 全程序优化 将这里的默认项 使用链接时间代码生成 改为 无全程序优化 xff0c 接下来就可以运行了
  • 指针常量和常量指针

    参考 xff1a C语言 常量指针 指针常量以及指向常量的指针常量三者区别详解 望崖的博客 CSDN博客 常量指针和指针常量的区别
  • LW_OOPC 宏配置及使用指南

    LW OOPC 宏配置及使用指南 摘抄 xff1a https github com Akagi201 lw oopc LW OOPC 是一套轻量级的面向对象 C 语言编程框架 它是一套 C 语言的宏 xff0c 总共 1 个 h 文件 如
  • 十个值得学习的c开源项目(嵌入式)

    开源世界有许多优秀的开源项目 xff0c 我选取其中十个最优秀的 最轻量级的C语言的项目 xff0c 希望可以为C语言开发人员提供参考 十个最值得阅读学习的C开源项目代码 1 Webbench 2 Tinyhttpd 3 cJSON 4 C
  • 树莓派开机无法进入桌面的解决办法

    1 初次开机会出现 34 raspi config 34 这个界面 xff0c xff08 如下图 xff09 如果不是初装的系统 xff0c 也可以输入命令 xff1a sudo raspi config xff0c 调出此界面 2 如果
  • Xshell 6, 7 已过期的解决方案

    公开版的Xshell一段时间后就评估失效 xff0c 很麻烦 xff0c 下面的方法可以在官网下载个人免费版本 xff0c 常用功能都有亲测有效 xff01 就算之前安装过已经过期的Xshell也没关系 1 首先进入 xff1a https
  • C语言网络编程(1)— UDP通信

    C语言网络编程 xff08 1 xff09 UDP通信 一 socket 我们要进行网络通信 xff0c 那么就要用到socket xff0c socket即网络套接字 xff0c 应用程序可以通过它发送或接收数据 xff0c 可对其进行像

随机推荐

  • C语言网络编程(2)— TCP通信

    C语言网络编程 xff08 2 xff09 TCP通信 一 TCP客户端 1 建立连接 我们要使用到socket xff0c 首先首先我们添加要使用的头文件 span class token macro property span clas
  • 搭建kubernetes v1.21.5 和 kubesphere v3.2.1

    一 准备一台干净的centos7 6机器 二 关闭防火墙打上相关补丁和相关系统软件 systemctl stop firewalld yum span class token function install span openssh op
  • js定时器

    一 定时器概述 开发时用到的js定时器主要分为两种 xff1a 1 一次性的定时器setTimeOut方法 xff0c 通过设置一定的时间 xff0c 时间到就执行 var timer 61 setTimeout fun 毫秒数 清除的方法
  • UNIX 环境高级编程

    与你共享 xff0c 与你共舞 xff01 UNIX环境高级编程 xff08 第3版 xff09 是被誉为UNIX编程 圣经 xff1b 书中除了介绍UNIX文件和目录 标准I O库 系统数据文件和信息 进程环境 进程控制 进程关系 信号
  • ROS的CMakeList编写

    参考这位博主 我的cmakelist包 在 home xxx catkin Drone src Flight Control ROS CMakeLists txt cmake minimum required span class toke
  • Ubuntu18.04 NX下用ZED2 双目立体相机进行SLAM

    NX下的ZED2开发 安装流程问题开始了看效果安装ZED2 ROS工具 新故事篇章 zed2测距开始实现 安装流程 了解zed参数 因为网上的安装流程还是不太完整 xff0c 我补充一下 希望对其他人也有帮助 部分流程参考这位 xff1a
  • ubuntu16.04备份和迁移

    ubuntu16 04备份和迁移 背景实践1 备份整个系统2 重装Ubuntu16 043 恢复系统 题外话 xff1a 修改主机名参考文章 背景 此文用来快速记录备份和恢复的过程步骤 xff0c 具体命令意思不做过多介绍 因为不想新设备重
  • Ubuntu apt-get报错

    昨天晚上更新源 xff0c 居然报错了 zcidcs 64 ubuntu sudo apt get upgrade sudo password for zcidcs Reading package lists Done Building d
  • 2014阿里巴巴面试总结

    刚结束的一面 xff0c 可能昨天笔试题目做得还行 xff0c 今天中午电话我叫我1 30去面试 xff0c 时间紧急 xff0c 我吃完饭赶紧回宿舍小休息一会儿 xff0c 然后奔赴文三路的华星时代大厦 人太多了 xff0c 等到了2 2
  • 基类指针,子类指针,虚函数,override与final

    一 xff1a 基类指针与子类指针 span class token macro property span class token directive keyword include span span class token strin
  • web开发中实现页面记忆的几种方式

    一 前言 在前段时间公司有个需求是对前一个页面的操作进行记忆 xff0c 例如分页的样式 xff0c 选中的条件等 之前是用的session去存储记忆数据 xff0c 老大让我调研一下目前比较合理的方式然后分析一下 xff0c 这里以本篇博
  • 基于VINS与FastPlanner的无人机自主飞行Gazebo仿真

    项目来源及展示 xff1a https www bilibili com video BV1WK4y1V7um from 61 search amp seid 61 12548150687335659873 基本思路 xff1a 采用Gaz
  • RGB-D SLAM 相关总结

    目录 一 RGB D SLAM是什么 xff1f 二 D435i说明 三 RGB D SLAM研究现状 1 现有的RGB D SLAM方法 1 1 前端 1 2 后端 1 3 闭环检测 1 4 制图 2 优秀RGB D SLAM介绍 2 1
  • VINS-Mono学习(一)——数据预处理

    void push back double dt const Eigen Vector3d amp acc const Eigen Vector3d amp gyr dt buf push back dt acc buf push back
  • VINS-Mono学习(四)——回环检测与重定位

    目录 1 闭环检测常用方法有哪些 xff1f 2 ORB SLAM2中Loop Closing的具体实现流程是怎样的 xff1f 3 VINS回环检测与重定位 四自由度位姿图优化 3 1 第一部分 xff1a 回环检测与重定位 3 1 1
  • LADRC的学习——总概

    作者 xff1a 墨心 日期 xff1a 2019 7 25 xff1b 学习LADRC结构 xff1a 1 学习PID的相关知识 xff0c 作为学习ADRC的基础铺垫 xff0c 在simulink中搭建模块 xff0c 通过调节参数
  • LADRC的学习——PID的学习

    PID部分的学习 上文介绍了ADRC的理论 xff0c 并试着按照自己的理解用Matab编程实现韩老师论文中的算法 xff0c 但是对调节参数和一些地方还不太懂 xff0c 因此我打算从头开始理解 xff0c 从PID的好坏开始学习理解 x
  • LADRC的学习——换被控对象进行仿真测试

    LADRC控制器的检验 用不同的被控对象测验 一 前文总结 这篇文章主要根据清华大学的硕士陈星写的论文 xff1a 自抗扰控制器参数整定方法及其热工过程中的应用 进行学习 参考文献为 xff1a 1 Zhiqiang Gao Scaling
  • ROS-3DSLAM(二)lvi-sam项目认识

    2021SC 64 SDUSC xff08 二 xff09 lvi sam项目认识 一 SLAM简介 SLAM是Simultaneous Localization and Mapping xff08 同时定位 43 建图 xff09 独立的
  • KMP算法——字符串匹配问题

    贴上原址 xff1a http www ruanyifeng com blog 2013 05 Knuth E2 80 93Morris E2 80 93Pratt algorithm html 感觉这篇文章讲得很不错 xff0c 很容易懂