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中使用第三方库xlrd来读取Excel示例
Apr 05 Python
python实现数据导出到excel的示例--普通格式
May 03 Python
python中reader的next用法
Jul 24 Python
python进行TCP端口扫描的实现
Dec 21 Python
python协程之动态添加任务的方法
Feb 19 Python
Python中psutil的介绍与用法
May 02 Python
在python中,使用scatter绘制散点图的实例
Jul 03 Python
python实现微信自动回复机器人功能
Jul 11 Python
Django框架 查询Extra功能实现解析
Sep 04 Python
Python线程条件变量Condition原理解析
Jan 20 Python
基于Tensorflow的MNIST手写数字识别分类
Jun 17 Python
tensorflow2.0教程之Keras快速入门
Feb 20 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中hashtable实现示例分享
2014/02/13 PHP
php通过exif_read_data函数获取图片的exif信息
2015/05/21 PHP
thinkPHP删除前弹出确认框的简单实现方法
2016/05/16 PHP
4种Windows系统下Laravel框架的开发环境安装及部署方法详解
2020/04/06 PHP
网页中可关闭的漂浮窗口实现可自行调节
2013/08/20 Javascript
jquery队列函数用法实例
2014/12/16 Javascript
JS中Eval解析JSON字符串的一个小问题
2016/02/21 Javascript
nodejs 的 session 简单使用
2016/06/06 NodeJs
灵活使用数组制作图片切换js实现
2016/07/28 Javascript
Bootstrapvalidator校验、校验清除重置的实现代码(推荐)
2016/09/28 Javascript
JavaScript如何实现图片懒加载(lazyload) 提高用户体验(增强版)
2016/11/30 Javascript
Vue.js学习笔记之修饰符详解
2017/07/25 Javascript
vue 插值 v-once,v-text, v-html详解
2018/01/19 Javascript
在vue项目中使用Nprogress.js进度条的方法
2018/01/31 Javascript
JS实现返回上一页并刷新页面的方法分析
2019/07/16 Javascript
JavaScript计算正方形面积
2019/11/26 Javascript
vue双击事件2.0事件监听(点击-双击-鼠标事件)和事件修饰符操作
2020/07/27 Javascript
[02:36]DOTA2上海特锦赛 回忆电竞生涯的重要瞬间
2016/03/25 DOTA
Python过滤列表用法实例分析
2016/04/29 Python
深入理解NumPy简明教程---数组1
2016/12/17 Python
Python基于SMTP协议实现发送邮件功能详解
2018/08/14 Python
python判断文件是否存在,不存在就创建一个的实例
2019/02/18 Python
使用Python获取当前工作目录和执行命令的位置
2020/03/09 Python
基于python实现matlab filter函数过程详解
2020/06/08 Python
如果重写了对象的equals()方法,需要考虑什么
2014/11/02 面试题
基于Python 函数和方法的区别说明
2021/03/24 Python
文秘专业个人求职信
2013/12/22 职场文书
会计试用期自我评价怎么写
2014/09/18 职场文书
怎样写家长意见
2015/06/04 职场文书
西游记读书笔记
2015/06/25 职场文书
任命书格式模板
2015/09/22 职场文书
pytorch中的model.eval()和BN层的使用
2021/05/22 Python
python基于机器学习预测股票交易信号
2021/05/25 Python
你知道Java Spring的两种事务吗
2022/03/16 Java/Android
MySQL中一条SQL查询语句是如何执行的
2022/04/08 MySQL
戴尔Win11系统no bootable devices found解决教程
2022/09/23 数码科技