python最长回文串算法


Posted in Python onJune 04, 2018

给定一个字符串,要求在这个字符串中找到符合回文性质的最长子串。所谓回文性是指诸如 “aba”,"ababa","abba"这类的字符串,当然单个字符以及两个相邻相同字符也满足回文性质。

看到这个问题,最先想到的解决方法自然是暴力枚举,通过枚举字符串所有字串的起点,逐一判断满足回文性的子串,记录长度并更新最长长度。显然这种算法的时间复杂度是很高的,最坏情况可以达到O(N*N)。所以呢,这里提出一个优化的方案,通过枚举字符串子串的中心而不是起点,向两边同时扩散,依然是逐一判断子串的回文性。这种优化算法比之前的算法在最坏的情况下(即只有一种字符的字符串)效率会有很大程度的上升。

由上述的优化方案,我们知道了枚举中心要比枚举起点效率要好,然而这并不是最优的算法。由于枚举中心的算法同时影响的是中心两边的字符,所以我们可以通过枚举中心的左边字符作为中心的子串的回文性判断枚举中心右边的字符作为中心得子串的回文性,这就是manacher算法。

manacher算法思想非常巧妙,首先遍历字符串,假设 i 为枚举中心,则 j (j<i) 为中心的最长回文子串长度发f[j] 便已经求出,此时 j 的影响范围便是[j-f[j]/2,j+f [j]] 。为了使左边的字符 j 对枚举中心右边的影响最大,需要使 j+f[j]/2 最大。找到满足j+f[j]/2最大的 j 之后,若 i 在[j,j+f[j]/2]中,则分两种情况:

1 . i 关于 j 对称的字符i'的影响范围完全包含在j的影响范围内,则由于回文性,i 的影响范围大于等于i'的影响范围,即f[i]>=f[i']

2. i 关于 j 对称的字符i'的影响范围不完全包含在j的影响范围内,此时i的右侧影响范围大于等于[j-f[j]/2,i'],即i+f[i]/2>=i'-j+f[j]/2

由于对称性,可得i+i" = 2*j。因此第一种情况下,f[i]>=f[2*j-i];第二种情况下,f[i]>=f[j]+2*j-2*i。

综上1,2,可得f[i]>=min(f[2*j-i],f[j]+2*j-2*i)。由于i右边存在未遍历的字符,因此在此基础上,继续向两边扩展,直到找到最长的回文子串。

若i依然在j+f[j]/2后面,则表示i没有被前面的字符的影响,只能逐一的向两边扩展。

这个算法由于只需遍历一遍字符串,扩展的次数也是有限的,所以时间复杂度可以达到O(N)。

下面是Pthon3的程序,为了检测算法的效率,依然提供最初的暴力枚举算法作为最坏算法的参照。

python代码:

#求最长回文串类 
class LPS:      
 #初始化,需要提供一个字符串 
 def __init__(self,string): 
  self.string = string 
  self.lens = len(self.string) 
  
 #暴力枚举:作为算法效率参照 
 def brute_force(self): 
  maxcount = 0 
  for j in range(self.lens):      
   for k in range(j,self.lens): 
    count = 0 
    l,m = j,k 
    while m>=l: 
     if self.string[l]==self.string[m]: 
      l,m = l+1,m-1 
     else: 
      break 
    if m<l: 
     count = k-j+1 
    if count>maxcount : 
     maxcount = count 
  return maxcount 
  
 #优化版:枚举子串中心 
 def brute_force_opti(self): 
  maxcount = 0 
  if self.lens == 1:        #只有一个字符直接返回1 
   return 1 
  for j in range(self.lens-1):     #枚举中心 
   count,u = 1,j 
   #对于奇数子串,直接扩展 
   for k in range(1,j+1):      #两边扩展 
    l,m = u+k,j-k 
    if (m>=0)&(l<self.lens): 
     if(self.string[l]==self.string[m]): 
      count += 2 
     else: 
      break 
   if count>maxcount :       #更新回文子串最长长度 
    maxcount = count 
   if self.string[j]==self.string[j+1]:  #处理偶数子串,将两个相邻相同元素作为整体 
    u,count= j+1,2 
   for k in range(1,j+1):      #两边扩展 
    l,m = u+k,j-k 
    if (m>=0)&(l<self.lens): 
     if(self.string[l]==self.string[m]): 
      count += 2 
     else: 
      break 
   if count>maxcount :       #更新回文子串最长长度 
    maxcount = count 
  return maxcount 
   
 #manacher算法 
 def manacher(self): 
  s = '#'+'#'.join(self.string)+'#'    #字符串处理,用特殊字符隔离字符串,方便处理偶数子串 
  lens = len(s) 
  f = []           #辅助列表:f[i]表示i作中心的最长回文子串的长度 
  maxj = 0          #记录对i右边影响最大的字符位置j 
  maxl = 0          #记录j影响范围的右边界 
  maxd = 0          #记录最长的回文子串长度 
  for i in range(lens):       #遍历字符串 
   if maxl>i:         
    count = min(maxl-i,int(f[2*maxj-i]/2)+1)#这里为了方便后续计算使用count,其表示当前字符到其影响范围的右边界的距离 
   else :          
    count = 1 
   while i-count>=0 and i+count<lens and s[i-count]==s[i+count]:#两边扩展 
    count +=1 
   if(i-1+count)>maxl:       #更新影响范围最大的字符j及其右边界 
     maxl,maxj = i-1+count,i               
   f.append(count*2-1) 
   maxd = max(maxd,f[i])      #更新回文子串最长长度 
  return int((maxd+1)/2)-1      #去除特殊字符

通过上面的程序,使用字符串为长度1000的纯‘a'字符串作为样例,经过测试:

暴力枚举:49.719844s

中心枚举:0.334019s

manacher:0.008000s

由此可见,长度为1000时,暴力枚举的耗时已经无法忍受了,而相比而言,中心枚举在效率上已经有很大幅度的提升,最优的manacher耗时则为更短。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python学习手册中的python多态示例代码
Jan 21 Python
简单介绍利用TK在Python下进行GUI编程的教程
Apr 13 Python
在Python中使用全局日志时需要注意的问题
May 06 Python
python使用线程封装的一个简单定时器类实例
May 16 Python
Django中利用filter与simple_tag为前端自定义函数的实现方法
Jun 15 Python
python实现彩票系统
Jun 28 Python
Python描述符descriptor使用原理解析
Mar 21 Python
python语言的优势是什么
Jun 17 Python
Django静态文件加载失败解决方案
Aug 26 Python
python Matplotlib模块的使用
Sep 16 Python
python opencv实现直线检测并测出倾斜角度(附源码+注释)
Dec 31 Python
用Python实现定时备份Mongodb数据并上传到FTP服务器
Jan 27 Python
python中字符串的操作方法大全
Jun 03 #Python
Python Logging 日志记录入门学习
Jun 02 #Python
python实现寻找最长回文子序列的方法
Jun 02 #Python
python实现对求解最长回文子串的动态规划算法
Jun 02 #Python
Python 网络爬虫--关于简单的模拟登录实例讲解
Jun 01 #Python
用Python一键搭建Http服务器的方法
Jun 01 #Python
python 编写简单网页服务器的实例
Jun 01 #Python
You might like
PHP生成静态页面详解
2006/12/05 PHP
一些php项目中比较通用的php自建函数的详解
2013/06/06 PHP
解析PHP中一些可能会被忽略的问题
2013/06/21 PHP
php通过function_exists检测函数是否存在的方法
2015/03/18 PHP
PHP几个实用自定义函数小结
2016/01/25 PHP
利用PHP判断是手机移动端还是PC端访问的函数示例
2017/12/14 PHP
JavaScript 函数调用规则
2009/09/14 Javascript
js实现收缩菜单效果实例代码
2013/10/30 Javascript
javascript中全局对象的isNaN()方法使用介绍
2013/12/19 Javascript
Javascript实现多彩雪花从天降散落效果的方法
2015/02/02 Javascript
js实现图片淡入淡出切换简易效果
2016/08/22 Javascript
vue.js加载新的内容(实例代码)
2017/06/01 Javascript
微信小程序 蓝牙的实现实例代码
2017/06/27 Javascript
深入理解vue2.0路由如何配置问题
2017/07/18 Javascript
Bootstrap图片轮播效果详解
2017/10/17 Javascript
Angular2管道Pipe及自定义管道格式数据用法实例分析
2017/11/29 Javascript
用vuex写了一个购物车H5页面的示例代码
2018/12/04 Javascript
使用vue for时为什么要key【推荐】
2019/07/11 Javascript
详解js location.href和window.open的几种用法和区别
2019/12/02 Javascript
Javascript如何实现双指控制图片功能
2020/02/25 Javascript
[01:21:58]守擂赛DOTA2第一周决赛
2020/04/22 DOTA
Python中asyncio模块的深入讲解
2019/06/10 Python
Django中URL的参数传递的实现
2019/08/04 Python
PyTorch之图像和Tensor填充的实例
2019/08/18 Python
Python实现朴素贝叶斯的学习与分类过程解析
2019/08/24 Python
完美解决pycharm导入自己写的py文件爆红问题
2020/02/12 Python
HTML5 Canvas 起步(2) - 路径
2009/05/12 HTML / CSS
使用canvas实现黑客帝国数字雨效果
2020/01/02 HTML / CSS
I.T集团香港官方商城:ITeSHOP.com Hong Kong
2019/02/15 全球购物
什么是岗位职责
2013/11/12 职场文书
个人承诺书
2014/03/26 职场文书
五年级小学生评语
2014/12/26 职场文书
入党积极分子个人总结
2015/03/02 职场文书
2016年小学生寒假总结
2015/10/10 职场文书
python APScheduler执行定时任务介绍
2022/04/19 Python
java开发双人五子棋游戏
2022/05/06 Java/Android