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


Posted in Python onApril 04, 2019

在看子串匹配问题的时候,书上的关于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,如下图所示:

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

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)

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

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

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)

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

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算法及python实现

临时数组构造完毕之后,就可以使用 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 也不会往后移动了),这里举一个例子帮助理解:

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

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

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

<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 -1
def 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 pnext
if __name__ == "__main__":
  string = 'abcxabcdabcdabcy'
  substring = 'abcdabcy'
  out = KMP_algorithm(string, substring)
  print(out)

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

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

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

快速传送门:戳我

总结

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

Python 相关文章推荐
python计算最大优先级队列实例
Dec 18 Python
python递归删除指定目录及其所有内容的方法
Jan 13 Python
python django事务transaction源码分析详解
Mar 17 Python
Python入门之后再看点什么好?
Mar 05 Python
python3模块smtplib实现发送邮件功能
May 22 Python
Python交互环境下实现输入代码
Jun 22 Python
python pcm音频添加头转成Wav格式文件的方法
Jan 09 Python
Python从文件中读取数据的方法讲解
Feb 14 Python
python实现基于朴素贝叶斯的垃圾分类算法
Jul 09 Python
tensorflow使用L2 regularization正则化修正overfitting过拟合方式
May 22 Python
tensorflow模型的save与restore,及checkpoint中读取变量方式
May 26 Python
Python基于pyjnius库实现访问java类
Jul 31 Python
Python魔法方法功能与用法简介
Apr 04 #Python
详解pandas.DataFrame中删除包涵特定字符串所在的行
Apr 04 #Python
pandas删除指定行详解
Apr 04 #Python
详解python之heapq模块及排序操作
Apr 04 #Python
python实现kmp算法的实例代码
Apr 03 #Python
详解python多线程之间的同步(一)
Apr 03 #Python
Python将列表数据写入文件(txt, csv,excel)
Apr 03 #Python
You might like
php随机生成数字字母组合的方法
2015/03/18 PHP
Symfony2实现在controller中获取url的方法
2016/03/18 PHP
Laravel使用PHPQRCODE实现生成带有LOGO的二维码图片功能示例
2017/07/07 PHP
Laravel框架使用Seeder实现自动填充数据功能
2018/06/13 PHP
laravel 框架配置404等异常页面
2019/01/07 PHP
Laravel 框架控制器 Controller原理与用法实例分析
2020/04/14 PHP
Javascript 个人笔记(没有整理,很乱)
2007/07/07 Javascript
[Web]防止用户复制页面内容和另存页面的方法
2009/02/06 Javascript
JS判断当前日期是否大于某个日期的实现代码
2012/09/02 Javascript
js为鼠标添加右击事件防止默认的右击菜单弹出
2013/07/29 Javascript
JS 获取浏览器和屏幕宽高等信息的实现思路及代码
2013/07/31 Javascript
js鼠标悬浮出现遮罩层的方法
2015/01/28 Javascript
js实现鼠标经过时图片滚动停止的方法
2015/02/16 Javascript
javascript实现数独解法
2015/03/14 Javascript
探讨AngularJs中ui.route的简单应用
2016/11/16 Javascript
jQuery实现动态生成表格并为行绑定单击变色动作的方法
2017/04/17 jQuery
JavaScript实现前端实时搜索功能
2020/03/26 Javascript
JavaScript函数表达式详解及实例
2017/05/05 Javascript
在Vue中如何使用Cookie操作实例
2017/07/27 Javascript
vue 将多个过滤器封装到一个文件中的代码详解
2020/09/05 Javascript
Vue使用Proxy代理后仍无法生效的解决
2020/11/13 Javascript
[53:10]完美世界DOTA2联赛决赛日 FTD vs GXR 第二场 11.08
2020/11/11 DOTA
举例讲解Python中is和id的用法
2015/04/03 Python
详解Python中类的定义与使用
2017/04/11 Python
python使用tcp实现局域网内文件传输
2020/03/20 Python
在scrapy中使用phantomJS实现异步爬取的方法
2018/12/17 Python
python将txt文件读取为字典的示例
2018/12/22 Python
Python新手学习标准库模块命名
2020/05/29 Python
毕业自我评价范文
2013/11/17 职场文书
2014年3.15团委活动总结
2014/03/16 职场文书
优质服务活动实施方案
2014/05/02 职场文书
学生会感恩节活动方案
2014/10/11 职场文书
立项申请报告范本
2015/05/15 职场文书
食堂管理制度范本
2015/08/04 职场文书
PHP连接MSSQL数据库案例,PHPWAMP多个PHP版本连接SQL Server数据库
2021/04/16 PHP
python 学习GCN图卷积神经网络
2022/05/11 Python