字符串匹配算法之 kmp算法 (python版)
字符串匹配算法之 kmp算法 (python版)
1.什么是KMP算法
KMP是三位大牛:D.E.Knuth、J.H.MorriT和V.R.Pratt同时发现的。其中第一位就是《计算机程序设计艺术》的作者!!
KMP算法要解决的问题就是在字符串(也叫主串)中的模式(pattern)定位问题。说简单点就是我们平时常说的关键字搜索。
KMP算法是用来求一个较长字符串是否包含另一个较短字符串的算法。
模式串就是关键字(接下来称它为P),如果它在一个主串(接下来称为T)中出现,就返回它的具体位置,否则返回-1(常用手段)。
2.暴力匹配算法
在研究KMP算法之前,先弄明白最直接、最暴力、最原始的匹配算法
举个例子,如果给定文本串T“BBC ABCDAB ABCDABCDABDE”,和模式串P“ABCDABD”,现在要拿模式串P去跟文本串T匹配,整个过程如下所示:
1. T[0]为B,P[0]为A,不匹配,执行第②条指令:“如果失配(即T[i]! = P[j]),令i = i - (j - 1),j = 0”,T[1]跟P[0]匹配,相当于模式串要往右移动一位(i=1,j=0)
2. T[1]跟P[0]还是不匹配,继续执行第②条指令:“如果失配(即T[i]! = P[j]),令i = i - (j - 1),j = 0”,T[2]跟P[0]匹配(i=2,j=0),从而模式串不断的向右移动一位(不断的执行“令i = i - (j - 1),j = 0”,i从2变到4,j一直为0)
3. 直到T[4]跟P[0]匹配成功(i=4,j=0),此时按照上面的暴力匹配算法的思路,转而执行第①条指令:“如果当前字符匹配成功(即T[i] == P[j]),则i++,j++”,可得T[i]为T[5],P[j]为P[1],即接下来T[5]跟P[1]匹配(i=5,j=1)
4. T[5]跟P[1]匹配成功,继续执行第①条指令:“如果当前字符匹配成功(即T[i] == P[j]),则i++,j++”,得到T[6]跟P[2]匹配(i=6,j=2),如此进行下去
5. 直到T[10]为空格字符,P[6]为字符D(i=10,j=6),因为不匹配,重新执行第②条指令:“如果失配(即T[i]! = P[j]),令i = i - (j - 1),j = 0”,相当于T[5]跟P[0]匹配(i=5,j=0)
6. 至此,我们可以看到,如果按照暴力匹配算法的思路,尽管之前文本串和模式串已经分别匹配到了T[9]、P[5],但因为T[10]跟P[6]不匹配,所以文本串回溯到T[5],模式串回溯到P[0],从而让T[5]跟P[0]匹配。
而T[5]肯定跟P[0]失配。为什么呢?因为在之前第4步匹配中,我们已经得知T[5] = P[1] = B,而P[0] = A,即P[1] != P[0],故T[5]必定不等于P[0],所以回溯过去必然会导致失配。那有没有一种算法,让i 不往回退,只需要移动j 即可呢?
答案是肯定的。这种算法就是本文的主旨KMP算法,它利用之前已经部分匹配这个有效信息,保持i 不回溯,通过修改j 的位置,让模式串尽量地移动到有效的位置。
3.KMP算法
KMP算法的核心要义在于next算法,构造next表,使用next表决定指针的跳转距离。
1. 假设现在已经根据模式串构造出了next表(可以是其他名字,比如 pnext表),考虑KMP算法的实现。
kmp算法主函数 核心匹配循环代码如下:
while j > n and i < m: # i == m 说明找到匹配
if i == -1 : # 遇到 -1 ,比较下一个字符
j , i = j + 1 , i + 1
elif t[j] == p[i] : # 字符相等,比较下一字符
j , i = j + 1 ,i + 1
else :
i = next[i] # 从next中取得p的下个字符的位置
优化:显然上面的代码中 两个if分支可以合并,代码如下:
while j > n and i < m: # i == m 说明找到匹配
if i == -1 or t[j] == p[i] : # 遇到 -1 ,比较下一个字符
j , i = j + 1 , i + 1
else :
i = pnext[i] # 从next中取得p的下个字符的位置
kmp算法主函数 代码如下:
def match_kmp(t,p,pnext):
''' KMP串匹配,主函数 '''
j , i = 0 , 0
n , m = len(t) , len(p)
while j > n and i < m: # i == m 说明找到匹配
if i == -1 or t[j] == p[i] : # 遇到 -1 ,比较下一个字符
j , i = j + 1 , i + 1
else :
i = pnext[i] # 从pnext中取得p的下个字符的位置 if i == m : # 匹配成功,返回其下标
return j - i
return -1 # 匹配失败,返回特殊值
2. pnext表的实现 (敲黑板,划重点)
先上代码:
def gen_pnext(p):
''' 生成针对指针p中各位置i的下一个检查的位置表,用于KMP算法 '''\
i , k , m = 0, -1 ,len(p) # k 即 pnext 表中的值
pnext = [-1] * m # 初始化 pnext 表
while i < m - 1:
if k == -1 or p[i] == p[k] # k = -1 代表 最长相等前后缀长度是0
i , k = i + 1 , k + 1
pnext[i] = k # 设置pnext元素
else :
k = pnext[k] # 遇到更短相同前缀
优化: 当 p[i] == p[k] 时,指针可以直接跳转到 k 位置(即pnext[k]), 代码修改如下:
def gen_pnext(p):
''' 生成针对指针p中各位置i的下一个检查的位置表,用于KMP算法 '''
i , k , m = 0, -1 ,len(p) # k 即 pnext 表中的值
pnext = [-1] * m # 初始化 pnext 表
while i < m - 1:
if k == -1 or p[i] == p[k] # k = -1 代表 最长相等前后缀长度是0
i , k = i + 1 , k + 1
pnext[i] = k # 设置pnext元素
if p[i] == p[k] # 这里进行了优化
pnext[i] = pnext[k]
else :
k = pnext[k] # 遇到更短相同前缀
return pnext
3. 时间复杂度
kmp 算法的时间复杂度是 O(m+n)
暴力匹配算法的时间复杂度是 O(m*n)
4.参考文章
http://www.cnblogs.com/en-heng/p/5091365.html
字符串匹配算法之 kmp算法 (python版)的更多相关文章
- KMP算法-Python版
KMP算法-Python版 传统法: 从左到右一个个匹配,如果这个过程中有某个字符不匹配,就跳回去,将模式串向右移动一位.这有什么难的? 我们可以 ...
- 动画演示Sunday字符串匹配算法——比KMP算法快七倍!极易理解!
前言 上一篇我用动画的方式向大家详细说明了KMP算法(没看过的同学可以回去看看). 这次我依旧采用动画的方式向大家介绍另一个你用一次就会爱上的字符串匹配算法:Sunday算法,希望能收获你的点赞关注收 ...
- Python 细聊从暴力(BF)字符串匹配算法到 KMP 算法之间的精妙变化
1. 字符串匹配算法 所谓字符串匹配算法,简单地说就是在一个目标字符串中查找是否存在另一个模式字符串.如在字符串 "ABCDEFG" 中查找是否存在 "EF" ...
- 字符串匹配算法之————KMP算法
上一篇中讲到暴力法字符串匹配算法,但是暴力法明显存在这样一个问题:一次只移动一个字符.但实际上,针对不同的匹配情况,每次移动的间隔可以更大,没有必要每次只是移动一位: 关于KMP算法的描述,推荐一篇博 ...
- 字符串匹配算法之kmp算法
kmp算法是一种效率非常高的字符串匹配算法,是由Knuth,Morris,Pratt共同提出的模式匹配算法,所以简称KMP算法 算法思想 在一个字符串中查找另一个字符串时,会遇到如下图的情况 我们通常 ...
- 字符串匹配算法(三)-KMP算法
今天我们来聊一下字符串匹配算法里最著名的算法-KMP算法,KMP算法的全称是 Knuth Morris Pratt 算法,是根据三位作者(D.E.Knuth,J.H.Morris 和 V.R.Prat ...
- 字符串匹配算法之Sunday算法(转)
字符串匹配算法之Sunday算法 背景 我们第一次接触字符串匹配,想到的肯定是直接用2个循环来遍历,这样代码虽然简单,但时间复杂度却是Ω(m*n),也就是达到了字符串匹配效率的下限.于是后来人经过研究 ...
- 数据结构学习之字符串匹配算法(BF||KMP)
数据结构学习之字符串匹配算法(BF||KMP) 0x1 实验目的 通过实验深入了解字符串常用的匹配算法(BF暴力匹配.KMP.优化KMP算法)思想. 0x2 实验要求 编写出BF暴力匹配.KM ...
- kmp算法python实现
kmp算法python实现 kmp算法 kmp算法用于字符串的模式匹配,也就是找到模式字符串在目标字符串的第一次出现的位置比如abababc那么bab在其位置1处,bc在其位置5处我们首先想到的最简单 ...
随机推荐
- BZOJ 3190 赛车 | 计算几何
BZOJ 3190 赛车 题面 这里有一辆赛车比赛正在进行,赛场上一共有N辆车,分别称为个g1,g2--gn.赛道是一条无限长的直线.最初,gi位于距离起跑线前进ki的位置.比赛开始后,车辆gi将会以 ...
- 51nod 1483 化学变换 | 二进制 暴力
51nod 1483 化学变换 题面 给出n个整数(n <= 1e5,每个数 <= 1e5),对每个数都可以进行多次操作,操作有两种:乘二/整除以二. 问最少经过多少次操作能使所有数相等. ...
- Linux命令之ipcalc
ipcalc命令是一个简单的ip地址计算器,可以完成简单的IP地址计算任务.参数: -b:由给定的IP地址和网络掩码计算出广播地址: -4:ipv4: -6:ipv6: -h:显示给定IP地址所对应的 ...
- CVE-2018-1111劫持dhcp造成centos代码执行漏洞
0x01 漏洞概述 近日,红帽官方发布了安全更新,修复了编号为CVE-2018-1111的远程代码执行漏洞,攻击者可以通过伪造DHCP服务器发送响应包,攻击红帽系统,获取root权限并执行任意命令. ...
- bzoj 3779: 重组病毒
一道好题~~ 一个点到根传染需要的时间是这段路径上不同颜色的数目,一个点子树到根平均传染时间就是加权平均数了(好像是废话). 所以只要用线段树维护dfs序就这个可以了,换根的话一个点的子树要么在dfs ...
- 「转」python数字图像处理(18):高级形态学处理
python数字图像处理(18):高级形态学处理 形态学处理,除了最基本的膨胀.腐蚀.开/闭运算.黑/白帽处理外,还有一些更高级的运用,如凸包,连通区域标记,删除小块区域等. 1.凸包 凸包是指一 ...
- 2017易观OLAP算法大赛
大赛简介 目前互联网领域有很多公司都在做APP领域的“用户行为分析”产品,与Web时代的行为分析相类似,其目的都是帮助公司的运营.产品等部门更好地优化自家产品,比如查看日活和月活,查看渠道来源 ...
- python set() 集合的添加删除、交集、并集、差集、交叉补集、集合的方法介绍以及使用案例
可变不可变: 1.可变:列表.字典.例如列表类型是可变的,我修改了列表中的元素的值,但是列表本身在内存中的地址是没有变化的,所以列表的元素是可以被改变的 >>> name=[&quo ...
- U33405 纽约
U33405 纽约 花费 \(w\) 元可以购买一辆容量为 \(w\) 的车 现在你有 \(n <= 2000\) 个物品, 搬运策略: 一直搬能放下里面最重的, 直到任意物品都不能搬上为止 求 ...
- 2017 清北济南考前刷题Day 1 afternoon
期望得分:80+30+70=180 实际得分:10+30+70=110 T1 水题(water) Time Limit:1000ms Memory Limit:128MB 题目描述 LYK出了道水 ...