侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

详解小白之KMP算法及python实现

Python  /  管理员 发布于 7年前   155

在看子串匹配问题的时候,书上的关于KMP的算法的介绍总是理解不了。看了一遍代码总是很快的忘掉,后来决定好好分解一下KMP算法,算是给自己加深印象。

在将KMP字串匹配问题的时候,我们先来回顾一下字串匹配的暴力解法:

假设字符串str为: "abcgbabcdh",  字串substr为: "abcd"

 从第一个字符开始比较,显然两个字符串的第一个字符相等('a'=='a'),然后比较第二个字符也相等('b'=='b'),继续下去,我们发现第4个字符不相等了('g'!='d'),这时候我们让'g'和字串的开头'a'比较,若两者相同,则同时后移一位比较下一个字母,不同则将str中比较的字符后移一位,然后和字串中开始的'a'比较。以此类推....我们可以在str中找到substr字串,并返回字串的位置。

这种暴力搜索方法很显然时间复杂度是O(m*n) n,m分别表示str字符串和substr字串的长度。m*n的复杂度显然是比较大的,当m或者n很大的时候,时间开销会很大。KMP算法则可以将时间复杂度下降到O(m+n),和O(m*n)相比明显下降。

KMP算法和暴力搜索方法之间的差别在于KMP算法在出现字符串不相等的情况时,不需要返回到字串的开头重新比较。

如何保证字符串不相等的情况出现时,字串不从最开始开始比较呢,这时候临时数组就登场了。

书本上总是介绍说是,判断此时字串中是否有相同前缀和后缀,懵逼脸......

看完临时数组是如何构造的你应该差不多就知道前后缀问题了。

** 临时数组 ** : 我们假设子串为 'abcabg', 开始时j指向第一个字符,i指向第二个字符(j=0, i=1)。并且令pnext[0] = 0,如下图所示:

1)  由于substr[j] != substr[i] 并且j=0, 令pnext[i] = 0 , i往后移一位。(步骤1后,j=0, i=2)

2)  由于substr[j] != substr[i] 并且j=0, 令pnext[i] = 0 , i往后移一位。(步骤2后,j=0, i=3)

3)  此时substr[j] == substr[i], 令pnext[i] = j + 1, 并且 i , j 都后移一位。(步骤3后,j=1,i=4)

这时候我们来看一下临时数组的状态:

4)  substr[j] == substr[i] 还是成立, 令pnext[i] = j+1,  并且i, j都后移一位。(j=2,  i=5)

5)  此时 substr[j] != substr[i],由于j=2(不为0),令j = pnext[j-1]  (由于pnext[j-1] = pnext[1] = 0 ==> j=0, 保持 i=5)

6)  substr[j] != substr[i], 并且j=0, 令pnext[i] = 0, 并使i后移一位。(j=0, i=6)

7)  substr[j] == substr[i],  同理pnext[i] = j+1 ,并且i, j都向后移动一位。(j=1, i=7)

8)  substr[j] != substr[i], j != 0, j = pnext[j-1] = pnext[0] = 0。 (j=0, i=7)

9)  substr[j] != substr[i], 且j=0, 令pnext[i] = 0。(此时i到达最后一个位置,并且pnext数组全部赋值完毕。pnext数组构造结束)

临时数组构造完毕之后,就可以使用 KMP算法 了。

还是假设 字符串str = 'abgabcabgacyf', 子串 substr = 'abcabgac'.

令i指向str的第一个字符,j指向substr第一个字符。KMP算法的详细运行步骤如下:

<1> str[i] == substr[j], i = i+1,  j = j+1. (步骤1之后: i=1, j=1)

<2> str[i] == substr[j], i = i+1, j = j+1. (i=2, j=2)

<3> str[i] != substr[j], 此时j != 0, 所以临时数组pnext就派上用场了。令 j = pnext[j-1].  (i=2,  j = pnext[2-1] = 0)

如果存在前后缀的话(即pnext[j-1]!=0),由于此步骤之前的substr与str相同(要不然 j 也不会往后移动了),这里举一个例子帮助理解:

如图,当i和j位于图中时刻,字符j与p不相等。(p之前的abcdab肯定和上面相等,要不然j不会移动到字符p上),按照暴力搜索的方法是不是要让j和子串的第一个字符a比较呢。KMP算法就不需要,我们可以看到子串中p之前的字符存在最大相等前后缀为'ab', 那在下一次比较的时候‘ab'是不是就不用比较了呢。从而直接比较j和c呢??(如下图)这就是KMP算法的精髓所在。

<4> 这时候str[i] != substr[j], 但是和步骤<3>不一样的是,此时j=0(由于pnext[-1]不存在,j不能等于pnext[j-1]了)。所以子串开头只能和str中下一个字符比较,即i = i+1。(i=3, j=0)

<5> str[i] == substr[j] ==> i = i+1, j = j+1. (i=4, j=1)

<6> 以此类推。这一过程存在两种方法中止,即i或者j不能再加1(加1就会发生越界的时候)。假设str的长度为n,substr的长度为m。当j==m时,说明找到了子串,否则没有找到。

def KMP_algorithm(string, substring):  '''  KMP字符串匹配的主函数  若存在字串返回字串在字符串中开始的位置下标,或者返回-1  '''  pnext = gen_pnext(substring)  n = len(string)  m = len(substring)  i, j = 0, 0  while (i<n) and (j<m):    if (string[i]==substring[j]):      i += 1      j += 1    elif (j!=0):      j = pnext[j-1]    else:      i += 1  if (j == m):    return i-j  else:    return -1def gen_pnext(substring):  """  构造临时数组pnext  """  index, m = 0, len(substring)  pnext = [0]*m  i = 1  while i < m:    if (substring[i] == substring[index]):      pnext[i] = index + 1      index += 1      i += 1    elif (index!=0):      index = pnext[index-1]    else:      pnext[i] = 0      i += 1  return pnextif __name__ == "__main__":  string = 'abcxabcdabcdabcy'  substring = 'abcdabcy'  out = KMP_algorithm(string, substring)  print(out)

 代码结果返回子串开始时的坐标位置。

看到这里如果还是没有懂得话,那就说明我表述的还不够好,推荐看看视频。

快速传送门:戳我

总结

以上所述是小编给大家介绍的详解小白之KMP算法及python实现,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对站的支持!


  • 上一条:
    Python使用sax模块解析XML文件示例
    下一条:
    Python魔法方法功能与用法简介
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在python语言中Flask框架的学习及简单功能示例(0个评论)
    • 在Python语言中实现GUI全屏倒计时代码示例(0个评论)
    • Python + zipfile库实现zip文件解压自动化脚本示例(0个评论)
    • python爬虫BeautifulSoup快速抓取网站图片(1个评论)
    • vscode 配置 python3开发环境的方法(0个评论)
    • 近期文章
    • 在windows10中升级go版本至1.24后LiteIDE的Ctrl+左击无法跳转问题解决方案(0个评论)
    • 智能合约Solidity学习CryptoZombie第四课:僵尸作战系统(0个评论)
    • 智能合约Solidity学习CryptoZombie第三课:组建僵尸军队(高级Solidity理论)(0个评论)
    • 智能合约Solidity学习CryptoZombie第二课:让你的僵尸猎食(0个评论)
    • 智能合约Solidity学习CryptoZombie第一课:生成一只你的僵尸(0个评论)
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2018-04
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2022-01
    • 2023-07
    • 2023-10
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客