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 相关文章推荐
采用Psyco实现python执行速度提高到与编译语言一样的水平
Oct 11 Python
Python实现的归并排序算法示例
Nov 21 Python
使用Python读取二进制文件的实例讲解
Jul 09 Python
django从请求到响应的过程深入讲解
Aug 01 Python
Python3.4学习笔记之 idle 清屏扩展插件用法分析
Mar 01 Python
PIL对上传到Django的图片进行处理并保存的实例
Aug 07 Python
docker-py 用Python调用Docker接口的方法
Aug 30 Python
python中调试或排错的五种方法示例
Sep 12 Python
基于python的selenium两种文件上传操作实现详解
Sep 19 Python
django模板获取list中指定索引的值方式
May 14 Python
Python操作Excel把数据分给sheet
May 20 Python
python 三种方法实现对Excel表格的读写
Nov 19 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递归获取目录内文件(包含子目录)封装类分享
2013/12/25 PHP
phpcms手机内容页面添加上一篇和下一篇
2015/06/05 PHP
PHP学习笔记之php文件操作
2016/06/03 PHP
php数组实现根据某个键值将相同键值合并生成新二维数组的方法
2017/04/26 PHP
JavaScript 语法集锦 脚本之家基础推荐
2009/11/15 Javascript
JavaScript 对象模型 执行模型
2010/10/15 Javascript
基于Bootstrap+jQuery.validate实现Form表单验证
2014/12/16 Javascript
原生js实现的贪吃蛇网页版游戏完整实例
2015/05/18 Javascript
javascript bom是什么及bom和dom的区别
2015/11/26 Javascript
基于javascript实现九宫格大转盘效果
2020/05/28 Javascript
浅谈jQuery效果函数
2016/09/16 Javascript
Android中Okhttp3实现上传多张图片同时传递参数
2017/02/18 Javascript
使用vue.js写一个tab选项卡效果
2017/03/25 Javascript
基于JS实现限时抢购倒计时间表代码
2017/05/09 Javascript
对vue中methods互相调用的方法详解
2018/08/30 Javascript
微信小程序实现顶部导航特效
2019/01/28 Javascript
JS学习笔记之原型链和利用原型实现继承详解
2019/05/29 Javascript
简述Vue中容易被忽视的知识点
2019/12/09 Javascript
VUE动态生成word的实现
2020/07/26 Javascript
python实现批量转换文件编码(批转换编码示例)
2014/01/23 Python
Python数组条件过滤filter函数使用示例
2014/07/22 Python
一波神奇的Python语句、函数与方法的使用技巧总结
2015/12/08 Python
Python中动态检测编码chardet的使用教程
2017/07/06 Python
在cmd中运行.py文件: python的操作步骤
2018/05/12 Python
python实现旋转和水平翻转的方法
2018/10/25 Python
详解Python:面向对象编程
2019/04/10 Python
python字符串的拼接方法总结
2019/11/18 Python
台湾前三大B2C购物网站:MOMO购物网
2017/04/27 全球购物
Ellesse英国官网:意大利高级运动品牌
2019/07/23 全球购物
SQL Server数据库笔试题和答案
2016/02/04 面试题
九年级科学教学反思
2014/01/29 职场文书
参赛口号
2014/06/16 职场文书
法人授权委托书样本
2014/09/19 职场文书
县委常委班子对照检查材料思想汇报
2014/09/28 职场文书
python本地文件服务器实例教程
2021/05/02 Python
聊聊Python String型列表求最值的问题
2022/01/18 Python