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笔记(2)
Oct 24 Python
Python探索之静态方法和类方法的区别详解
Oct 27 Python
python3+PyQt5实现自定义流体混合窗口部件
Apr 24 Python
Python 统计字数的思路详解
May 08 Python
python计算两个数的百分比方法
Jun 29 Python
Python解决线性代数问题之矩阵的初等变换方法
Dec 12 Python
python reverse反转部分数组的实例
Dec 13 Python
对python判断是否回文数的实例详解
Feb 08 Python
Python+Selenium使用Page Object实现页面自动化测试
Jul 14 Python
django最快程序开发流程详解
Jul 19 Python
Python爬虫使用代理IP的实现
Oct 27 Python
python argparse模块通过后台传递参数实例
Apr 20 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 smarty模版引擎中的缓存应用
2009/12/11 PHP
PHP XML操作类DOMDocument
2009/12/16 PHP
PHP对MongoDB[NoSQL]数据库的操作
2013/03/01 PHP
php的ajax简单实例
2014/02/27 PHP
php计算title标题相似比的方法
2015/07/29 PHP
ECSHOP完美解决Deprecated: preg_replace()报错的问题
2016/05/17 PHP
php禁用cookie后session设置方法分析
2016/10/19 PHP
php+croppic.js实现剪切上传图片功能
2018/08/14 PHP
js对象转json数组的简单实现案例
2014/02/28 Javascript
JavaSacript中charCodeAt()方法的使用详解
2015/06/05 Javascript
JavaScript模块规范之AMD规范和CMD规范
2015/10/27 Javascript
JavaScript创建对象的方式小结(4种方式)
2015/12/17 Javascript
基于BootStrap的图片轮播效果展示实例代码
2016/05/23 Javascript
深入浅析JS Function()构造函数
2016/08/22 Javascript
简单实现Vue的observer和watcher
2016/12/21 Javascript
JS基于正则截取替换特定字符之间字符串操作示例
2017/02/03 Javascript
nodejs学习笔记之路由
2017/03/27 NodeJs
javascript内存分配原理实例分析
2017/04/10 Javascript
Iphone手机、安卓手机浏览器控制默认缩放大小的方法总结(附代码)
2017/08/18 Javascript
JavaScript数组的5种迭代方法
2017/09/29 Javascript
快速搭建vue2.0+boostrap项目的方法
2018/04/09 Javascript
react 应用多入口配置及实践总结
2018/10/17 Javascript
JS原生瀑布流效果实现
2019/04/26 Javascript
在微信小程序中使用mqtt服务的方法
2019/12/13 Javascript
jquery实现拖拽添加元素功能
2020/12/01 jQuery
django用户注册、登录、注销和用户扩展的示例
2018/03/19 Python
Python绘制3D图形
2018/05/03 Python
Python 中如何实现参数化测试的方法示例
2019/12/10 Python
详解Python 函数参数的拆解
2020/09/02 Python
什么是类的返射机制
2016/02/06 面试题
班组长岗位职责
2014/03/03 职场文书
幼儿园教师岗位职责
2014/03/17 职场文书
相亲大会策划方案
2014/06/05 职场文书
结婚当天新郎保证书
2015/05/08 职场文书
详解Python内置模块Collections
2022/03/22 Python
我家女友可不止可爱呢 公开OP主题曲无字幕动画MV
2022/04/11 日漫