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数组条件过滤filter函数使用示例
Jul 22 Python
Python中lambda的用法及其与def的区别解析
Jul 28 Python
Python实现的径向基(RBF)神经网络示例
Feb 06 Python
详解python-图像处理(映射变换)
Mar 22 Python
使用python将mysql数据库的数据转换为json数据的方法
Jul 01 Python
基于Django ORM、一对一、一对多、多对多的全面讲解
Jul 26 Python
Python3批量移动指定文件到指定文件夹方法示例
Sep 02 Python
Python Collatz序列实现过程解析
Oct 12 Python
Python如何读取文件中图片格式
Jan 13 Python
解决echarts中饼图标签重叠的问题
May 16 Python
使用Keras建立模型并训练等一系列操作方式
Jul 02 Python
详解python 支持向量机(SVM)算法
Sep 18 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
php设计模式 Command(命令模式)
2011/06/26 PHP
php调用新浪短链接API的方法
2014/11/08 PHP
PHP获取当前完整URL地址的函数
2014/12/21 PHP
PHP 根据key 给二维数组分组
2016/12/09 PHP
PHP基于ORM方式操作MySQL数据库实例
2017/06/21 PHP
PHP实现的迪科斯彻(Dijkstra)最短路径算法实例
2017/09/16 PHP
PHP设计模式之 策略模式Strategy详解【对象行为型】
2020/05/01 PHP
用javascript动态调整iframe高度的方法
2007/03/06 Javascript
JQuery从头学起第三讲
2010/07/06 Javascript
jQuery自带的一些常用方法总结
2014/09/03 Javascript
jquery实现图片放大镜功能
2015/11/23 Javascript
JS jQuery使用正则表达式去空字符的简单实现代码
2017/05/20 jQuery
angular directive的简单使用总结
2017/05/24 Javascript
更强大的vue ssr实现预取数据的方式
2019/07/19 Javascript
24个解决实际问题的ES6代码片段(小结)
2020/02/02 Javascript
ES6中的类(Class)示例详解
2020/12/09 Javascript
SpringBoot+Vue 前后端合并部署的配置方法
2020/12/30 Vue.js
wxPython中文教程入门实例
2014/06/09 Python
python3生成随机数实例
2014/10/20 Python
Python paramiko模块使用解析(实现ssh)
2019/08/30 Python
python错误调试及单元文档测试过程解析
2019/12/19 Python
利用Tensorflow构建和训练自己的CNN来做简单的验证码识别方式
2020/01/20 Python
Python列表操作方法详解
2020/02/09 Python
学点简单的Django之第一个Django程序的实现
2021/02/24 Python
微软香港官网及网上商店:Microsoft HK
2016/09/01 全球购物
elf彩妆英国官网:e.l.f. Cosmetics英国(美国平价彩妆品牌)
2017/11/02 全球购物
英国地毯卖家:The Rug Seller
2019/07/18 全球购物
分层教学实施方案
2014/03/19 职场文书
小学校长先进事迹材料
2014/05/13 职场文书
企业文化宣传标语
2014/06/09 职场文书
中秋节国旗下演讲稿
2014/09/05 职场文书
爸爸的三轮车观后感
2015/06/16 职场文书
如何在centos上使用yum安装rabbitmq-server
2021/03/31 Servers
vue2实现provide inject传递响应式
2021/05/21 Vue.js
深入理解java.lang.String类的不可变性
2021/06/27 Java/Android
如何让你的Nginx支持分布式追踪详解
2022/07/07 Servers