字符串匹配的kmp算法 及 python实现
一:背景
给定一个主串(以 S 代替)和模式串(以 P 代替),要求找出 P 在 S 中出现的位置,此即串的模式匹配问题。
Knuth-Morris-Pratt 算法(简称 KMP)是解决这一问题的常用算法之一,这个算法是由高德纳(Donald Ervin Knuth)和沃恩 · 普拉特在 1974 年构思,同年詹姆斯 ·H· 莫里斯也独立地设计出该算法,最终三人于 1977 年联合发表。
在继续下面的内容之前,有必要在这里介绍下两个概念:真前缀 和 真后缀。

由上图所得, "真前缀" 指除了自身以外,一个字符串的全部头部组合;"真后缀" 指除了自身以外,一个字符串的全部尾部组合。
二:朴素字符串匹配算法
初遇串的模式匹配问题,我们脑海中的第一反应,就是朴素字符串匹配(即所谓的暴力匹配)
暴力匹配的时间复杂度为 O(nm),其中 n 为 S 的长度,m 为 P 的长度。很明显,这样的时间复杂度很难满足我们的需求。
接下来进入正题:时间复杂度为 Θ(n+m) 的 KMP 算法。
三:KMP 字符串匹配算法
3.1 算法流程
以下摘自阮一峰的字符串匹配的 KMP 算法,并作稍微修改。
(1)

首先,主串 "BBC ABCDAB ABCDABCDABDE" 的第一个字符与模式串 "ABCDABD" 的第一个字符,进行比较。因为 B 与 A 不匹配,所以模式串后移一位。
(2)

因为 B 与 A 又不匹配,模式串再往后移。
(3)

就这样,直到主串有一个字符,与模式串的第一个字符相同为止。
(4)

接着比较主串和模式串的下一个字符,还是相同。
(5)

直到主串有一个字符,与模式串对应的字符不相同为止。
(6)

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

一个基本事实是,当空格与 D 不匹配时,你其实是已经知道前面六个字符是 "ABCDAB"。KMP 算法的想法是,设法利用这个已知信息,不要把 "搜索位置" 移回已经比较过的位置,而是继续把它向后移,这样就提高了效率。
(8)
| i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|---|
| 模式串 | A | B | C | D | A | B | D | '\0' |
| next[i] | -1 | 0 | 0 | 0 | 0 | 1 | 2 | 0 |
怎么做到这一点呢?可以针对模式串,设置一个跳转数组int next[],这个数组是怎么计算出来的,后面再介绍,这里只要会用就可以了。
(9)

已知空格与 D 不匹配时,前面六个字符 "ABCDAB" 是匹配的。根据跳转数组可知,不匹配处 D 的 next 值为 2,因此接下来从模式串下标为 2 的位置开始匹配。
(10)

因为空格与C不匹配,C 处的 next 值为 0,因此接下来模式串从下标为 0 处开始匹配。
(11)

因为空格与 A 不匹配,此处 next 值为 - 1,表示模式串的第一个字符就不匹配,那么直接往后移一位。
(12)

逐位比较,直到发现 C 与 D 不匹配。于是,下一步从下标为 2 的地方开始匹配。
(13)

逐位比较,直到模式串的最后一位,发现完全匹配,于是搜索完成。
3.2 next 数组是如何求出的展开目录
next 数组的求解基于 “真前缀” 和 “真后缀”,即next[i]等于P[0]...P[i - 1]最长的相同真前后缀的长度(请暂时忽视 i 等于 0 时的情况,下面会有解释)。我们依旧以上述的表格为例,为了方便阅读,我复制在下方了。
| i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|---|
| 模式串 | A | B | C | D | A | B | D | '\0' |
| next[i] | -1 | 0 | 0 | 0 | 0 | 1 | 2 | 0 |
- i = 0,对于模式串的首字符,我们统一为
next[0] = -1; - i = 1,前面的字符串为
A,其最长相同真前后缀长度为 0,即next[1] = 0; - i = 2,前面的字符串为
AB,其最长相同真前后缀长度为 0,即next[2] = 0; - i = 3,前面的字符串为
ABC,其最长相同真前后缀长度为 0,即next[3] = 0; - i = 4,前面的字符串为
ABCD,其最长相同真前后缀长度为 0,即next[4] = 0; - i = 5,前面的字符串为
ABCDA,其最长相同真前后缀为A,即next[5] = 1; - i = 6,前面的字符串为
ABCDAB,其最长相同真前后缀为AB,即next[6] = 2; - i = 7,前面的字符串为
ABCDABD,其最长相同真前后缀长度为 0,即next[7] = 0。
那么,为什么根据最长相同真前后缀的长度就可以实现在不匹配情况下的跳转呢?举个代表性的例子:假如i = 6时不匹配,此时我们是知道其位置前的字符串为ABCDAB,仔细观察这个字符串,首尾都有一个AB,既然在i = 6处的 D 不匹配,我们为何不直接把i = 2处的 C 拿过来继续比较呢,因为都有一个AB啊,而这个AB就是ABCDAB的最长相同真前后缀,其长度 2 正好是跳转的下标位置。
python实现,如下:
def partial_table(p):
'''''partial_table("ABCDABD") -> [0, 0, 0, 0, 1, 2, 0]'''
prefix = set()
postfix = set()
ret = [0]
for i in range(1, len(p)):
prefix.add(p[:i])
postfix = {p[j:i + 1] for j in range(1, i + 1)}
ret.append(len((prefix & postfix or {''}).pop()))
return ret
print partial_table("ABCDABD")
#[0, 0, 0, 0, 1, 2, 0]
全部代码:
#coding=utf-8
def kmp_match(s, p):
m = len(s);
n = len(p)
cur = 0 # 起始指针cur
table = partial_table(p)
while cur <= m - n: #只去匹配前m-n个
for i in range(n):
if s[i + cur] != p[i]:
cur += max(i - table[i - 1], 1) # 有了部分匹配表,我们不只是单纯的1位1位往右移,可以一次移动多位
break
else: #for 循环中,如果没有从任何一个 break 中退出,则会执行和 for 对应的 else
#只要从 break 中退出了,则 else 部分不执行。
return True
return False # 部分匹配表
def partial_table(p):
'''''partial_table("ABCDABD") -> [0, 0, 0, 0, 1, 2, 0]'''
prefix = set()
postfix = set()
ret = [0]
for i in range(1, len(p)):
prefix.add(p[:i])
postfix = {p[j:i + 1] for j in range(1, i + 1)}
ret.append(len((prefix & postfix or {''}).pop()))
return ret print partial_table1("ABCDABD") print kmp_match("BBC ABCDAB ABCDABCDABDE", "ABCDABD")
参考 如何理解 KMP
字符串匹配的kmp算法 及 python实现的更多相关文章
- Luogu 3375 【模板】KMP字符串匹配(KMP算法)
Luogu 3375 [模板]KMP字符串匹配(KMP算法) Description 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置. 为了减少骗分的情况,接下来 ...
- 字符串匹配的 KMP算法
一般字符串匹配过程 KMP算法是字符串匹配算法的一种改进版,一般的字符串匹配算法是:从主串(目标字符串)和模式串(待匹配字符串)的第一个字符开始比较,如果相等则继续匹配下一个字符, 如果不相等则从主串 ...
- HDU 1711 Number Sequence (字符串匹配,KMP算法)
HDU 1711 Number Sequence (字符串匹配,KMP算法) Description Given two sequences of numbers : a1, a2, ...... , ...
- 字符串匹配(KMP 算法 含代码)
主要是针对字符串的匹配算法进行解说 有关字符串的基本知识 传统的串匹配法 模式匹配的一种改进算法KMP算法 网上一比較易懂的解说 小样例 1计算next 2计算nextval 代码 有关字符串的基本知 ...
- 实现字符串匹配的KMP算法
KMP算法是Knuth-Morris-Pratt算法的简称,它主要用于解决在一个长字符串S中匹配一个较短字符串s. 首先我们从整体来把我这个算法的思想. 字符串匹配的朴素算法: 我们容易想到朴素算法, ...
- 字符串匹配的KMP算法
~~~摘录 来源:阮一峰~~~ 字符串匹配是计算机的基本任务之一. 举例来说,有一个字符串”BBC ABCDAB ABCDABCDABDE”,我想知道,里面是否包含另一个字符串”ABCDABD”? 许 ...
- 字符串匹配的KMP算法详解及C#实现
字符串匹配是计算机的基本任务之一. 举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另一个字符串"ABCDABD" ...
- 字符串匹配与KMP算法实现
>>字符串匹配问题 字符串匹配问题即在匹配串中寻找模式串是否出现, 首先想到的是使用暴力破解,也就是Brute Force(BF或蛮力搜索) 算法,将匹配串和模式串左对齐,然后从左向右一个 ...
- 字符串匹配的KMP算法(转)
转载:http://kb.cnblogs.com/page/176818/ 字符串匹配是计算机的基本任务之一. 举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE&quo ...
随机推荐
- 百度搜索 “Java面试题” 前200页(面试必看)
前言 本文中的题目来源于网上的一篇文章<百度搜索 "Java面试题" 前200页>,但该文章里面只有题目,没有答案.因此,我整理了一些答案发布于本文.本文整理答案的原则 ...
- HGOI20190126 模拟赛
/* 最后一题比较难! */ solution:观察这个奇怪的图,不能共用走廊,就是1.2打包,3,4打包,每个包之间连线的线段覆盖问题. 考虑吧每个数映射成一个约为一半的数,且相邻(前奇后偶映射值一 ...
- Mac安装mysql8.0.12
···shell 下载 wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.12-macos10.13-x86_64.tar.gz ...
- 解题:国家集训队 Crash 的文明世界
题面 这种套着高次幂的统计问题一般都要用到第二类斯特林数和自然数幂的关系:$a^k=\sum\limits_{i=0}^{k}S_k^iC_a^i*i!$ 那么对于每个点$x$有: $ans_x=\s ...
- Redis在window上安装
转:https://www.cnblogs.com/M-LittleBird/p/5902850.html 在windows上启动RabbitMQ: 这是Redis的启动,需要制定配置文件,否则连接不 ...
- vue ssr服务端渲染
SSR:Server Side Rendering(服务端渲染) 目的是为了解决单页面应用的 SEO 的问题,对于一般网站影响不大,但是对于论坛类,内容类网站来说是致命的,搜索引擎无法抓取页面相关内容 ...
- JDK1.7安装和环境配置
进行Java或者Android开发,首先必须要搭建一个开发环境,先安装JDK,然后配置jdk环境变量.JDK现在的最高版本为JDK 1.8 ,如果我们要做Android开发,我们则需要JDK1.7以上 ...
- bzoj千题计划277:bzoj4513: [Sdoi2016]储能表
http://www.lydsy.com/JudgeOnline/problem.php?id=4513 f[i][0/1][0/1][0/1] 从高到低第i位,是否卡n的上限,是否卡m的上限,是否卡 ...
- [整理]ASP.NET MVC 5
1.入门 1.1官方资料 http://www.asp.net/mvc/overview/getting-started/introduction/getting-started 疑问: startu ...
- 【问题收集·中级】关于XMPP使用Base传送图片
[问题收集·中级]关于XMPP使用Base传送图片 下面是我与博友的问答过程:并在最后链接附录了相应的文件: 博友问题: 16:35:38 他跟我说要 内容图片 base64编码 上传..博友问题 ...