求最长回文串-从动态规划到马拉车之路(上)

要解决的问题:

给定一个字符串,要求求出这个字符串中的最长的回文串子串。

例子:

cbddba的最长回文子串为 bddb

cbdedba的最长回文子串为dbedb

由上面的例子可以看到,在考虑回文子串的问题时需要考虑奇偶性。因为奇回文关于中心的某个字符对称,而偶回文关于最中心的两个元素之间的间隙对称。

一、动态规划法

在动态规划的思想中,总是希望把问题划分成相关联的子问题;然后从最基本的子问题出发来推导较大的子问题,直到所有的子问题都解决。

首先要看一个较大的子问题与一个较小的子问题之间的关系:

首先建立如下的函数:


那么就能有如下的递推关系:

当p(i+1,j-1)  = true 的时候,如果有si == sj,那么 p(i,j)=true;也就是 abba中的bb为回文串,那么在bb左边是a,在bb右边也是a,相同;所以有abba也是回文串。

形式化表示如下: p(i,j) = p(i+1,j-1) and si==sj    这就建立了较大问题与较小问题之间的关系。

然后考虑基本情况,一个最基本的回文串有两种情况(奇偶性):

(1) 最基本的奇回文,只有一个字符,而且恒成立;形式化表示为 p(i,i) = true

(2)最基本的偶回文,有两个字符,当且仅当两个字符相等的时候成立p(i,j) = (si==sj and j=i+1)

最后获得如下判断一个字符是否为回文串的分段函数:


有了公式,就是用代码实现了;下面给出了我的python实现。

# -*- coding:utf-8 -*-
# Author: Evan Mi# 测试的字符串
str_exp = "babad"
# 用来保存动态规划过程的表 1表示true 0表示false
longest_palindromes = [[-1] * len(str_exp) for i in range(len(str_exp))]
# longest_len 用来保存最长的回文串的长度
longest_len = 1
# 从长度为1的回文子串开始填表
for p_len in range(1, len(str_exp)+1):for i in range(len(str_exp)):j = p_len + i - 1if j < len(str_exp):if i == j:longest_palindromes[i][j] = 1elif j == i + 1 and str_exp[i] == str_exp[j]:longest_palindromes[i][j] = 1longest_len = p_lenelif j > i+1 and longest_palindromes[i+1][j-1] == 1 and str_exp[i] == str_exp[j]:longest_palindromes[i][j] = 1longest_len = p_lenelse:longest_palindromes[i][j] = 0
# 搜索结果表,打印出所有的最优解
for i in range(len(str_exp)):for j in range(len(str_exp)):if longest_palindromes[i][j] == 1 and j-i+1 == longest_len:print(str_exp[i:j+1])

在动态规划中,最最最核心的就是填表了,就以程序中的例子"babad"举例,说明一下填表的过程;

首先我们要填的表是如下的一张表二维表(因为p函数中有i,j两个变量),其中绿色的部分是真实的表格,其他的是我家的解释表头。


填表过程如下:首先填长度为1的;

 

   然后填长度为2的;

 

   接着是长度为3的:


  长度为4的:


  最后是长度为5的:


填表完成之后,求最优解就是查询了;对于时间复杂度,动态规划的时间复杂度在构建表的过程中的基本操作,所以时间复杂度是O(n^2);空间复杂度,就是上面的二维数组,也是O(n^2)。而且从在空间浪费(主对角线下面的空间没有使用,浪费了一般的空间)。有很多针对空间上的优化方法,下面给出一种空间复杂度为O(1)的算法;


二、空间复杂度为O(1)的算法


该算法的主要思想就遍历所有的字符(下标为i)是以第i个数(对应奇回文)或者第i个数和第i+1个数之间的间隙(对应偶回文)为中心向两边扩展,直到扩展以后不再是回文,那么就停止扩展,如果回文长度比已知的最长回文长,那么记录下该回问的开始位置和结束位置为最长回文;

还是用"babad"来作为例子,过程如下:



代码实现如下:

# -*- coding:utf-8 -*-
# Author: Evan Mi
import mathdef expandAroundCenter(left, right, s):"""从left和right之间开始扩展,如果left==right就是以left/right为中心进行扩展"""rLeft = leftrRight = rightwhile rLeft >= 0 and rRight < len(s) and s[rLeft] == s[rRight]:  # 进行扩展rLeft -= 1rRight += 1"""针对于返回的长度,因为在while循环停止的时候,rLeft和rRight都已经在要求的回文串之外了所以回文串的长度为rRight - rLeft - 1,自己可以画个过程图,一目了然。"""return rRight - rLeft - 1s = "babad"
start = 0
end = 0for i in range(len(s)):odd_len = expandAroundCenter(i, i, s)  # i为中心的扩展even_len = expandAroundCenter(i, i + 1, s)  # i 和 i+1之间的空隙为中心进行扩展lens = max(odd_len, even_len)  # 取得本次扩展的最大值if lens > (end-start+1):  # 如果本次的长度比记录的回文的长度也就是end-start+1大,进行替换# 需要注意的是,已经知道了位置i,不管是以i为中心扩展了长为lens的回文还是# 以i和i+1的空隙为中心扩展了长为lens的回文。下面的start和end的计算方法都成立start = i - math.floor((lens - 1) / 2) end = i + math.floor(lens/2)print(start, ":", end)
print(s[start:end+1])


这里时间复杂度并没有改变,但是我们的空间复杂度变成了O(1)。但是也带来了明显的缺陷,那就是只能求的第一个出现的最优解。 额,如果把 if lens > (end-start+1) 改为 if lens >= (end-start+1),那么能求最后出现的最优解。

这两个算法不论在空间、结果上有什么不同,它们的时间复杂度都是相同的;接下来就分析一下“马拉车”算法,该算法把时间复杂度降到了线性范围内。额,请见下篇博客。

PS:很多人都分析了"马拉车"算法,但是也阻挡不了我征服它的步伐。相信自己对它一定有独到的见解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/569209.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

为什么Python中称__lt__、__gt__等为“富比较”方法

Python中基类object提供了一系列可以用于实现同类对象进行“比较”的方法&#xff0c;可以用于同类对象的不同实例进行比较&#xff0c;包括__lt__、__gt__、__le__、__ge__、__eq__和__ne__六个方法。 那么为什么叫“富比较”(“rich comparison”)方法呢&#xff1f;查了相关…

求最长回文串-从动态规划到马拉车之路(下)

预备知识&#xff1a; &#xff08;1&#xff09;在一个数轴上有两点i和j&#xff08;i<j&#xff09;关于点m对称&#xff0c;那么有 i 2m-j&#xff1b; 证明&#xff1a; 因为 i<j 且 i 和 j 关于 m 对称&#xff0c;那么有 &#xff08;i j&#xff09;/ 2 m 所…

项目管理实战之团队管理 (转自:zhuweisky)

一个系统不仅需要优秀的分析和设计&#xff0c;更需要一个良好的过程将其从蓝图转化为实现。这个过程中最重要的是对团队的管理&#xff0c;也就是人的管理。一个优秀的团队和一个糟糕的团队的效能是天壤之别&#xff0c;她们之间的比例不是1:100或1:1000这样量化的数字能够表示…

python3 内置方法

# -*- coding:utf-8 -*- # Author: Evan Mi import functools # 取绝对值 print(abs:, abs(-1)) # 如果一个可迭代对象的所有元素都为真&#xff0c;返回true ;空也返回真 print(all:, all([1, 0, -3])) # 有一个为真就全为真 print(any:, any([1, 0, -1])) # 变成可打印的字符…

JS 职责链模式

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title></head><script>/*职责链模式 所有对象依次处理请求&#xff0c;1不能处理传给2&#xff0c;2不能处理传给3....*//*场景 打折 100以下不打折&…

python3字符串常用操作

# -*- coding:utf-8 -*- # Author: Evan Miname "my name is alex"print(name.capitalize()) # 首字母大写 print(name.count("a")) # 统计整个字符中a的个数 print(aaaaa.count("a", 0, len(aaaaa)-1)) # 前闭后开 print(name.center(50, &…

通过NGINX location实现一个域名访问多个项目

location ~ \.php$ {   root /home/webroot; //此目录下有多个项目 project1 &#xff0c;project2...   fastcgi_pass $php_upstream;   fastcgi_index index.php;   include fastcgi.conf; } location ~/project1 {   index index.php;   fastcgi_pass $php_u…

python3 set相关操作

# -*- coding:utf-8 -*- # Author: Evan Mi# 创建一个set list_1 [1, 3, 5, 7, 3, 6, 7, 9] list_1 set(list_1) list_2 set([2, 6, 0, 66, 22, 8, 4]) print(list_1, type(list_1))# 交集 print(list_1.intersection(list_2)) print(list_1 & list_2) # 并集 print(lis…

JDK环境变量

下载打开如下链接&#xff1a;http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html &#xff0c;进入JDK1.8下载官网&#xff0c;或者直接百度JDK1.8&#xff0c;也可进入下载官网。 进入官网后&#xff0c;按照所给信息下载对应系统版本的J…

python3字典相关方法

# -*- coding:utf-8 -*- # Author: Evan Miinfo {stu1101: TengLan Wu,stu1102: LongZe Luola,stu1103: XiaoZe Maliya } # 字典是无序的 print(info) print(info[stu1101]) # 不存在会报错 print(info.get(stu1101)) # 不存在返回None print(stu1103 in info) # 判断是否…

Shadow Defender 语言文件并注册

:: ::关闭回显 echo off ::设置标题 title 覆盖 Shadow Defender 语言文件并注册:: ::获取管理员权限 set "_FilePath%~f0" set "_FileDir%~dp0" setlocal EnableExtensions EnableDelayedExpansion fltmc >nul 2>&1 || (echo Set UAC CreateOb…

python3 列表相关操作

# -*- coding:utf-8 -*- # Author: Evan Mi import copynames ["ZhangYang", "Guyun", "XiangPeng", "XuliangChen"] #创建一个列表 names.append("LeiHaiDong") # 给列表的末尾追加元素 names.insert(1, "ChenRongHu…

NickLee 多層菜單

void InitMenu(){ //初始化菜單 MenuItem menuFirst; DataSet dsPermit; UserInfo ui ; DataSet dsFfunc.GetDataTable("select * from cqsSystemTree where F_Parent000 and isValid1 order by showSort"); foreach (DataRow myrow in dsF.Tables[0].Rows…

python3 生成器

要说生成器&#xff0c;就必须首先要知道列表的概念&#xff1b; 我们创建一个如下的列表&#xff1a; ls [1,2,3,4,5,6,7,8,9] 那么就开辟了一个门牌号为ls的内存区&#xff0c;然后真的把1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5&#xff0c;6&#xff0c;7&…

递归 与 动态规划 区别

递归 &#xff1a; 后面的子问题由前面的子问题解来表示 f(n) _f(n-1) f(n-2)等来表示 动态规划&#xff1a;前面的解由后面的子问题解来选择&#xff0c;自底向上&#xff0c;后面的解层层向前 得到最前的解。 key&#xff1a;找到dp[n] 与 dp[n-1]的联系&#xff0c;也就…

python3 文件相关操作

# -*- coding:utf-8 -*- # Author: Evan Mi""" data open("E:/pythondata/day02/yesterday.txt").read() print(data) """ # r是读模式&#xff0c;找不到文件会报错 r 在读的基础上有了写的能力&#xff0c;这里的写就是追加 # w是…

libvirt虚拟机管理常用指令

创建虚拟机 virt-install virt-install --connect qemu:///system -n $NAME -r $MEM -f $DISK -s $DISK_SIZE --vnc --vnclisten0.0.0.0 --os-typelinux --os-variantcentos7 --vcpus2 --network bridgebr0 -c $ISO --force 其中DISK_SIZE以G为单位&#xff0c;MEM以MB为单位&a…

Copy: 了解SQL Server锁争用:NOLOCK 和 ROWLOCK 的秘密

From http://blog.csdn.net/Atwind/archive/2007/10/19/1832844.aspx 关系型数据库&#xff0c;如SQL Server&#xff0c;使用锁来避免多用户修改数据时的并发冲突。当一组数据被某个用户锁定时&#xff0c;除非第一个用户结束修改并释放锁&#xff0c;否则其他用户就无法修改…

python3 中方法各种参数和返回值

# -*- coding:utf-8 -*- # Author: Evan Mi# 函数def func1():print(in the func1)return 0# 过程def func2():print(in the func2)""" 多个值用逗号分割后返回&#xff0c;会分装到一个tuple中返回&#xff0c; 接收的时候&#xff0c;如果使用一个变量接收&am…

react-json渲染

在js文件内 //定义react组件 import React from react; import ReactDom from react-dom import ./components/assets/taobao.cssclass TaoBao extends React.Component{state{list:[{title:女装,href:javescript:;,hot:false,child:[{title:衬衫,href:javescript:;,hot:false}…