[Algorithm] 字符串匹配算法——KMP算法

1 字符串匹配

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

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

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

2 KMP算法

  这个算法不太容易理解,网上有很多解释,但读起来都很费劲。直到读到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"的位置。

  算法时间复杂度为O(m+n)(其中m为字符段长度,n为匹配模式的长度)。

3 算法实现

void getNext(const std::string &p, std::vector<int> &next)
{next.resize(p.size());next[0] = -1;int i = 0, j = -1;while (i != p.size() - 1){//这里注意,i==0的时候实际上求的是next[1]的值,以此类推if (j == -1 || p[i] == p[j]){++i;++j;next[i] = j;}else{j = next[j];}}
}int kmp(const std::string& s, const std::string& p, const int sIndex = 0)
{std::vector<int>next(p.size());getNext(p, next);//获取next数组,保存到vector中int i = sIndex, j = 0;while(i != s.length() && j != p.length()){if (j == -1 || s[i] == p[j]){++i;++j;}else{j = next[j];}}return j == p.length() ? i - j: -1;
}

   相关内容:kmp算法实现原理及简单示例。

转载于:https://www.cnblogs.com/maybe2030/p/4633153.html

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

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

相关文章

入门Git

本文是我在[实验楼]这个平台学习git时的第一篇学习笔记&#xff0c;现贴出来以飨大家&#xff01; git学习 1、git的配置 git的配置主要通过git config --global <配置名称> <配置的值>来对git进行配置 其中最常用的配置为&#xff1a; git config --global u…

小程序广告变现:探索创新路径实现盈利

随着移动互联网的不断发展&#xff0c;小程序作为一种轻量级应用形式&#xff0c;在用户中的普及程度不断提升。对于开发者而言&#xff0c;如何在小程序中实现盈利成为了一项挑战&#xff0c;而广告变现成为其中一种常见的经济模式。本文将深入探讨小程序广告变现的方式以及如…

服务器共享文件审计,内网安全管理系统-共享审计

在现代企事业单位的网络中&#xff0c;最常用的功能莫过于“共享文件”了。财务部门需要当月员工的考勤信息&#xff0c;人事部门可能不会亲自拿过去&#xff0c;而是在网络上共享&#xff1b;生产部门的生产报表也不会用书面的资料分发&#xff0c;而是放在网络的共享文件夹下…

介绍“Razor”— ASP.NET的一个新视图引擎

我的团队当前正在从事的工作之一就是为ASP.NET添加一个新的视图引擎。 一直以来&#xff0c;ASP.NET MVC都支持 “视图引擎”的概念—采用不同语法的模板的可插拔模块。当前ASP.NET MVC “默认”的视图引擎是ASP.NET Web窗体使用的.aspx/.ascx/.master文件模板。而当今其他一些…

w10系统打印服务器怎样出来,win10怎么打开关闭打印机服务教程步骤

当我们想要使用打印机服务时候&#xff0c;却不知道在哪里打开&#xff0c;对于win10系统&#xff0c;具体怎么操作呢?下面小编来告诉你开启和关闭打印机服务的方法吧&#xff0c;希望对你有帮助!Win10系统开启和关闭打印机服务的方法1、在Win10系统下&#xff0c;按住键盘的“…

获取函数的名字

c99标准中的__func__预定义标识符功能可以帮我们获取函数的名称 #include<string> #include<iostream> using namespace std;const char *hello(){return __func__; }int main(){cout<<hello()<<endl;return 0; }代码中的函数相当于&#xff1a; con…

浅谈自学方法论- 不断更新-记录思路

1. 用程序员的思想&#xff0c;去自学。 从主函数入手&#xff0c;也就是&#xff0c;了解整个框架。 2. 读书&#xff0c;带着宏观和微观的思路&#xff0c; 先不管看得懂看不懂看第一遍&#xff0c; 然后带着问题去读第二遍&#xff0c;并搜索不懂得关键词。 第三遍&#xff…

xp系统目前禁用索引服务器,WinXP系统中可以被禁用的服务对照表

application layer gateway service为internet连接共享和internet连接防火墙提供第三方协议插件的支持如果你没启用internet连接共享或windows xp内置防火墙&#xff0c;可以禁止这个服务。automatic updates自动从windows update启用windows更新的下载和安装需要时&#xff0c…

hadoop之linux常用命令

Linux的命令后面会有命令选项&#xff0c;有的选项还有选项值。选项的前面有短横线“-”&#xff0c;命令、选项、选项值之间使用空格隔开。有的命令没有选项&#xff0c;会有参数。选项是命令内置的功能&#xff0c;参数是用户提供的符合命令格式的内容。 1.1.1. 命…

c获取文件的名字和运行到程序的第几行功能

可以通过__FILE__和__LINE__两个宏获取文件的名字和代码运行的行数 #include<stdio.h> int main(){printf("file:%s line:%d\n",__FILE__,__LINE__);return 0; }__FILE__在linux中能获取到文件名称&#xff0c;但是在windows中获取的是带路径的名字。

MongoDB系列二

简介 MongoDB是一个基于分布式文件存储的数据库。由C语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。 MongoDB是一个高性能&#xff0c;开源&#xff0c;无模式的文档型数据库&#xff0c;是当前NoSql数据库中比较热门的一种。 MongoDB是一个介于关系数据库和非关…

通过查看__cplusplus的值查看编译器的C++标准

C03标准中&#xff0c;__cplusplus被定义为199711L&#xff0c;而在C11中&#xff0c;__clpusplus则被定义为201103L #include<iostream> using namespace std; int main(){cout<<__cplusplus<<endl;return 0; }

Oracle-数据实现竖排打印

--存放重证评分的数据表create table ZZPFapache2( ZZ_datetime DATE, --时间 ZZ_zongfen INTEGER, --总分 ZZ_shiwanglui INTEGER, --死亡率 ZZ_BINGRENID VARCHAR2(50), --病人ID ZZ_h1f1 INTEGER, --第1行1个分 ZZ_h1m1 VARCHAR2(40), ZZ_h1f2 INTEGER, --第1行…

C#时间格式

可以这样写: date.ToString("yyyy年MM月", DateTimeFormatInfo.InvariantInfo) 日期转化二 DateTime dt DateTime.Now; Label1.Text dt.ToString();//2005-11-5 13:21:25 Label2.Text dt.ToFileTime().ToString();//127756416859912816 Label3.Text dt.ToFileTim…

C++11的静态断言

断言就是将一个返回值总是需要为真的判别式放在语句中&#xff0c;来排除在设计的逻辑上不应该出现的情况。C11标准中引入了静态断言&#xff1a;static_assert 在C标准中&#xff0c;<cassert>或assert.h为我们提供了assert宏&#xff0c;但是这个宏只有在运行时才进行…

C++ 字符串编程训练2

今天讲的一道习题是很经典的约瑟夫环问题&#xff0c;其实lz对于链表的某些操作还不是太懂&#xff0c;所以在程序中有些地方还不太看得懂&#xff0c;这里借鉴的网上的做法&#xff0c;还请大牛能够解答我的疑惑&#xff0c;谢谢&#xff01; 标题&#xff1a;约瑟夫环 说明&a…

linux扩展lvm磁盘

env&#xff1a; centos 6.5 x64 hyper-v虚拟机 这个方法可以在当前运行的系统中扩展root磁盘 详细步骤 之前想创建的一个虚拟机的磁盘空间不够用了&#xff0c;所以想扩容一下磁盘。 正好使用的时候是lvm磁盘&#xff0c;可以支持扩容。 格式化一个新的分区或者磁盘 Command…

C/C++编译、测试须知、须会,CMake、Boost等

以下内容为本人实习期间学习笔记&#xff01;&#xff01;参考了网上的许多教程&#xff0c;共享大家&#xff0c;欢迎交流。 动态库和静态库&#xff08;共享库&#xff09; 不同点&#xff1a;代码被载入的时刻不同 静态库的代码在编译过程中已经被载入可执行程序&#xf…

C# DataTable去除重复,极其简便、简单

其中sourceDT是获取到的一个DataTable类型的集合对象 去重复使用方式&#xff1a; 实例化一个DataView对象 假设为dv&#xff0c;直接dv.ToTable()即可&#xff0c;ToTable中可为&#xff08;true,"用于判断重复的列"&#xff09;&#xff0c;比如图中所示&#xff0…

【转】C++类中对同类对象private成员访问

私有成员变量的概念&#xff0c;在脑海中的现象是&#xff0c;以private关键字声明&#xff0c;是类的实现部分&#xff0c;不对外公开&#xff0c;不能在对象外部访问对象的私有成员变量&#xff0e; 然而&#xff0c;在实现拷贝构造函数和赋值符函数时&#xff0c;在函数里利…