微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

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,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") 

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用爬虫利器 Playwright,轻松爬取抖查查数据 我们先分析登录的接口,其中 url 有一些非业务参数:ts、he、sign、secret。 然后根据这些参数作为关键词,定位到相关的 js 代码。 最后,逐步进行代码的跟踪,发现大部分的代码被混淆加密了。 花费了大半天,来还原这些混淆加密的代码
轻松爬取灰豚数据的抖音商品数据 调用两次登录接口实现模拟登录 我们分析登录接口,发现调用了两次不同的接口;而且,需要先调用 https://login.huitun.com/weChat/userLogin,然后再调用 https://dyapi.huitun.com/userLogin 接口。 登
成功绕过阿里无痕验证码,一键爬取飞瓜数据 飞瓜数据的登录接口,接入了阿里云的无痕验证码;通过接口方式模拟登录,难度比较高。所以,我们使用自动化的方式来实现模拟登录,并且获取到 cookie 数据。 [阿里无痕验证码] https://help.aliyun.com/document_detail/1
一文教你从零开始入门蝉妈妈数据爬取,成功逆向破解数据加密算法 通过接口进行模拟登录 我们先通过正常登录的方式,分析对应的登录接口。通过 F12 打开谷歌浏览器的调试面板,可以看到登录需要传递的一些参数;其中看到密码是被加密了。 不过我们通过经验可以大概猜测一下,应该是通过 md5 算法加密了。 接下
抽丝剥茧成功破解红人点集的签名加密算法 抽丝剥茧破解登录签名算法,成功实现模拟登录 headers = {} phone_num = &quot;xxxx&quot; password = &quot;xxxx&quot; md5_hash = hashlib.md5() md5_hash.upda
轻松绕过 Graphql 接口爬取有米有数的商品数据 有米有数数据的 API 接口,使用的是一种 API 查询语言 graphql。所有的 API 只有一个入口,具体的操作隐藏在请求数据体里面传输。 模拟登录,获取 sessionId 调用登录接口,进行模拟登录。 cookies = {} head
我最近重新拾起了计算机视觉,借助Python的opencv还有face_recognition库写了个简单的图像识别demo,额外定制了一些内容,原本想打包成exe然后发给朋友,不过在这当中遇到了许多小问题,都解决了,记录一下踩过的坑。 1、Pyinstaller打包过程当中出现warning,跟d
说到Pooling,相信学习过CNN的朋友们都不会感到陌生。Pooling在中文当中的意思是“池化”,在神经网络当中非常常见,通常用的比较多的一种是Max Pooling,具体操作如下图: 结合图像理解,相信你也会大概明白其中的本意。不过Pooling并不是只可以选取2x2的窗口大小,即便是3x3,
记得大一学Python的时候,有一个题目是判断一个数是否是复数。当时觉得比较复杂不好写,就琢磨了一个偷懒的好办法,用异常处理的手段便可以大大程度帮助你简短代码(偷懒)。以下是判断整数和复数的两段小代码: 相信看到这里,你也有所顿悟,能拓展出更多有意思的方法~
文章目录 3 直方图Histogramplot1. 基本直方图的绘制 Basic histogram2. 数据分布与密度信息显示 Control rug and density on seaborn histogram3. 带箱形图的直方图 Histogram with a boxplot on t