KMP算法精解及其Python版的代码示例


Posted in Python onJune 01, 2016

KMP算法是经典的字符串匹配算法,解决从字符串S,查找模式字符串M的问题。算法名称来源于发明者Knuth,Morris,Pratt。
假定从字符串S中查找M,S的长度ls,M的长度lm,且(ls > lm)。

朴素的字符串查找方法
从字符串S的第一个字符开始与M进行比较,如果匹配失败。从下一字符开始,重新比较。指导第 (ls - lm) 个字符。
这种方法容易想到并且容易理解,效率不高。
问题在于每次匹配失败后,移动的步伐固定为 1,其实步子可以迈得再大一些。

KMP的字符串查找方法
假定在模式串的连续字串M[0, i] 且 i < lm,已经成功匹配字符串S。但是不巧第 i+1 个字符失败了,怎么办?移动一个字符,重头再来?当然不好,那就是朴素路线了。我们能否从跌倒的地方继续走呢?
既然字串M[0 - i]已经匹配成功,那就从这个子串上做文章。举个栗子     

S序号 j j + 1  j + 2 j + 3 j + 4 j + 5  j+6 j + 7 。。。
S串 a b c a b c d e 。。。
M串 a b c a b d
M序号 0 1 2 3 4 5

此时匹配失败在M串的第5个字符,前4个字符已经匹配成功。
如果从跌倒的地方出发,则需要存在M[0, 4]的子串M[0, k] == S[j+4-k , j+4]。
由于M[0, 4] == S[j ,  j+4] 则有 字串S[j+4-k, j+4] == M[4-k, 4]。综上有M[0, k] == M[4-k, 4]
如果这样的k不存在,那就老老实实的朴素了。
从上面的表格可以直观的看出,下一次匹配只要把M串移动到 j + 3 位置,从 j+5 开始匹配就可以。很容易看出来 在已经匹配成功的字串M[0 , 4]中有最长的子串 (M[0 , 1] == M[3 , 4]),这个就是问题的关键。
因此KMP的核心部分就是计算模式串的各个子串的 k。

实例
首先我们来看一下字符串的朴素匹配.
可以想象成把文本串s固定住,模式串p从s最左边开始对齐,如果对齐的部分完全一样,则匹配成功,失败则将模式串p整体往右移1位,继续检查对齐部分,如此反复.

#朴素匹配 
def naive_match(s, p): 
 m = len(s); n = len(p) 
 for i in range(m-n+1):#起始指针i 
  if s[i:i+n] == p: 
   return True 
 return False

关于kmp算法,讲的最好的当属阮一峰的<字符串匹配的KMP算法>.一路读下来,豁然开朗.
其实就是,对模式串p进行预处理,得到前后缀的部分匹配表,使得我们可以借助已知信息,算出可以右移多少位.即 kmp = 朴素匹配 + 移动多位.
更多细节请看阮一峰的文章,这里就不展开了.
下面给出python的代码实现.

#KMP 
def kmp_match(s, p): 
 m = len(s); n = len(p) 
 cur = 0#起始指针cur 
 table = partial_table(p) 
 while cur<=m-n: 
  for i in range(n): 
   if s[i+cur]!=p[i]: 
    cur += max(i - table[i-1], 1)#有了部分匹配表,我们不只是单纯的1位1位往右移,可以一次移动多位 
    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 naive_match("BBC ABCDAB ABCDABCDABDE", "ABCDABD") 
print partial_table("ABCDABD") 
print kmp_match("BBC ABCDAB ABCDABCDABDE", "ABCDABD")

Python 相关文章推荐
python在命令行下使用google翻译(带语音)
Jan 16 Python
python一行sql太长折成多行并且有多个参数的方法
Jul 19 Python
Python面向对象之静态属性、类方法与静态方法分析
Aug 24 Python
Python设计模式之外观模式实例详解
Jan 17 Python
Pyqt清空某一个QTreeewidgetItem下的所有分支方法
Jun 17 Python
python超时重新请求解决方案
Oct 21 Python
爬虫代理池Python3WebSpider源代码测试过程解析
Dec 20 Python
Python中 Global和Nonlocal的用法详解
Jan 20 Python
Django ModelForm操作及验证方式
Mar 30 Python
Pandas之缺失数据的实现
Jan 06 Python
能让Python提速超40倍的神器Cython详解
Jun 24 Python
Python编程中内置的NotImplemented类型的用法
Mar 23 Python
Python缩进和冒号详解
Jun 01 #Python
Python注释详解
Jun 01 #Python
深入理解python try异常处理机制
Jun 01 #Python
python学习 流程控制语句详解
Jun 01 #Python
python+Django+apache的配置方法详解
Jun 01 #Python
python中函数默认值使用注意点详解
Jun 01 #Python
Python中基础的socket编程实战攻略
Jun 01 #Python
You might like
source.php查看源文件
2006/12/09 PHP
PHP中使用imagick生成PSD文件缩略图教程
2015/01/26 PHP
PHP使用Pthread实现的多线程操作实例
2015/11/14 PHP
WordPress特定文章对搜索引擎隐藏或只允许搜索引擎查看
2015/12/31 PHP
php阳历转农历优化版
2016/08/08 PHP
JavaScript 模仿vbs中的 DateAdd() 函数的代码
2007/08/13 Javascript
JavaScript 克隆数组最简单的方法
2009/02/12 Javascript
JQuery 无废话系列教程(一) jquery入门 [推荐]
2009/06/23 Javascript
用js实现判断当前网址的来路如果不是指定的来路就跳转到指定页面
2011/05/02 Javascript
js实现浏览器的各种菜单命令比如打印、查看源文件等等
2013/10/24 Javascript
浅析javascript的return语句
2015/12/15 Javascript
JavaScript+html5 canvas绘制的圆弧荡秋千效果完整实例
2016/01/26 Javascript
Bootstrap每天必学之导航条(二)
2016/03/01 Javascript
封装获取dom元素的简单实例
2016/07/08 Javascript
Javascript创建类和对象详解
2017/05/31 Javascript
Bootstrap fileinput文件上传组件使用详解
2017/06/06 Javascript
JavaScript创建对象_动力节点Java学院整理
2017/06/27 Javascript
JavaScript原型链与继承操作实例总结
2018/08/24 Javascript
jQuery实现的鼠标拖动画矩形框示例【可兼容IE8】
2019/05/17 jQuery
JQuery样式与属性设置方法分析
2019/12/07 jQuery
vue3.0 的 Composition API 的使用示例
2020/10/26 Javascript
Node使用koa2实现一个简单JWT鉴权的方法
2021/01/26 Javascript
python算法学习之桶排序算法实例(分块排序)
2013/12/18 Python
python中使用百度音乐搜索的api下载指定歌曲的lrc歌词
2014/07/18 Python
简单的编程0基础下Python入门指引
2015/04/01 Python
探究Python中isalnum()方法的使用
2015/05/18 Python
Win7下Python与Tensorflow-CPU版开发环境的安装与配置过程
2018/01/04 Python
Python 读取图片文件为矩阵和保存矩阵为图片的方法
2018/04/27 Python
Pandas的read_csv函数参数分析详解
2019/07/02 Python
python接口自动化框架实战
2020/12/23 Python
教师年终个人自我评价
2013/10/04 职场文书
标准单位租车协议书
2014/09/23 职场文书
孝老爱亲事迹材料
2014/12/24 职场文书
基于angular实现树形二级表格
2021/10/16 Javascript
Python用any()函数检查字符串中的字母以及如何使用all()函数
2022/04/14 Python
Linux在两个服务器直接传文件的操作方法
2022/08/05 Servers