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

预备知识:

(1)在一个数轴上有两点i和j(i<=j)关于点m对称,那么有 i = 2m-j;

 证明: 因为 i<=j 且 i 和 j 关于 m 对称,那么有 (i + j)/ 2 = m

  所以 i = 2m - j;

(2)回文串的对称性:

 由回文串的定义可以知道,奇回文关于最中心的数对称,而偶回文关于最中心的两个数之间的间隔对称。

根据回文串的对称性质,可以得到如下图所示的有用结论:


 如图所示,A是一个从0到N的回文串,且M是回文串A的中心,那么就能推出字符串

AL[0...f(M)] ==AR[g(M)...N],其中f(M)为对M向下取整,g(M)为对M向上取整;

 那么如果图中的B是回文串(B包含在A的左半区内),且以I为中心;而且J和I关于M对称,

那么以J为中心一定存在一个回文串C,且C和B完全相同;

 推论:


 如上图所示,如果J关于M的对称点I为中心的回文串的最左端B0超出了回文串A的最左端A0,

那么我们就无法肯定地说一定存在一个以J为中心的回文串C,且C与B完全相同了(因为[B0,A0)

范围内的字符在回文A以外,不受对称性的控制)。但是如果我们舍弃B的[B0,A0)部分,也就是取

上图中的[A0,A0']为以I为中心的回文串B';那么就能确定以J为中心的[AN',AN]也是回文串,

[AN',AN]和[A0,A0']完全相同(其中的AN'和A0'也关于M对称)。

(3) 把奇回文和偶回文的分类考察统一为一种考察

 这是一种技巧性的处理方式,给定了一个字符串babad;

在考察babad为是不是回文的时候,我们要考虑它是否关于最中心的b对称(即是否为ba|b|ad),

在考察子串baba是否为回文串的时候,要考虑是否关于最中心的ab之间的间隙对称(即是否为ba|ba);

这就使得我们需要分情况去讨论字符串是否为回文串。

 现在对字符串做如下的处理,华丽变身为如下的字符串#b#a#b#a#d#,对与这个字符串,

你要确定它本身是不是回文串,依然需要分成奇回文和偶回文来讨论。但是如果你通过它来确定它的

生成源(babad)是否为回文串,那么就只需要按奇回文的情况来考虑就可以了。额,具体来说就是,

如果我考虑#b#a#b#a#d#是否以某个#为中心是回文串的时候,实际就是考察生成源是否以两个字母之间

的间隙为中心为回文串(偶回文情况);如果我考虑#b#a#b#a#d#是否以某个非#的字符为中心的回文串时,

实际就是考察生成源是否是以该字母为中心的回文串(奇回文情况);

 用上篇中提到的扩展法来处理#b#a#b#a#d#,且只考虑奇回文情况,那么可以得到下表:


 从表中我们看出,在只考虑奇回文的情况下,求的的每个回文串的长度设为L;那么有生成源的回文的长度为(L-1)/2;并且长度为L的回文子串的开始字符和结束字符在该字符串中的下标分别设为S和E;

那么S/2向下取整为对应的生成源的回文子串在生成源中的开始下标;(E-1)/2向下

取整为对应的生成源的回文子串在生成源中的结束下标。

例如(#b#a#b#a#d#)中第6行有#a#b#a#的回文子串长度为7,开始下标为2,结束下标为8

 那么就对应于生成源(babad)的回文子串aba长度为3,开始下标为1,结束下标为3。


“马拉车算法”

 首先定义长度为奇数的字符串S的半径为r(S) = (S.length()-1)/2 ;

如下图所示:


 然后定义输入 radses ,radses[i]用来存放已经求的的以i为中心的奇回文的半径r;

 再定义, current_center,current_rights_index分别为已经求得半径的奇回文中结束下标最大

的奇回文的中心,最右下标。为什么要记录这个量,其实是为了尽可能的取减少重

复计算(也就是尽可能的利用预备知识中的(2));因为current_rights_index越大,

自加后i在它的左侧的可能就越大;这样就更可能的对这个i利用预备知识(2),

直接确定一个以i为中心的较大的初始半径,减少比较次数。

 定义,result_center,result_rads所求的最长的回文串的中心和半径;

代码实现如下:

# -*- coding:utf-8 -*-
# Author: Evan Mi
import math
# 要测试的字符串
test_str = 'babad'
# enriched_len 是指#b#a#b#a#d#的长度
enriched_len = len(test_str)*2 + 1#
radses = [0]*enriched_lencurrent_rights_index = 0
current_center = 0result_rads = 0
result_center = 0for i in range(enriched_len):   # 遍历所有增强后的字符串#b#a#b#a#d# 这里我并没有真的构建该字符串,只是假装有一个if current_rights_index > i:"""如过i在current_rights_index的左侧,那么就可以利用预备知识(2)来确定更长的初始半径symmetric_i = 2*current_center - i 预备知识(1) 求出i关于current_center对称的点的下标radses[i] = min(radses[symmetric_i], current_rights_index - i)当i在current_rights_index的左侧时,预备知识(2)或其推论,必然有一个成立;current_rights_index - i(推论中的AN - J 由对称性 也是 I - A0)是推论成立时的半径,radses[symmetric_i]是预备知识(2)成立时的情况。这里用min,读者可以从预备知识(2)中的图上看出来,推论成立的时候一定是current_rights_index - i小,而反之则是radses[symmetric_i];当然也可能一起成立,相等取最小也是OK的。"""symmetric_i = 2*current_center - iradses[i] = min(radses[symmetric_i], current_rights_index - i)else:radses[i] = 0"""i - (radses[i] + 1) >= 0 半径向左扩展1,不超出列表范围i + (radses[i] + 1) < enriched_len 半径向右扩展1,不超出列表范围((i - (radses[i] + 1)) % 2 == 0 and (i + (radses[i] + 1)) % 2 == 0) 扩展后的最左端和最右端的字符都是#这里要说明,在给一个字符串填充#以后,所有的下标为偶数的字符都是#,大家可以自己证明。(test_str[math.floor((i - (radses[i] + 1))/2)] == test_str[math.floor((i + (radses[i] + 1))/2)]))在被#号填充后的字符串中,所有不是#号的字符,也就是来自源字符串的字符的下标除以2,向下取整,就是对应与该字符在源字符串中的下标。第三和第四个条件的或组合,其实就是判断半径扩展一以后两端的字符是否相同。这里因为并没有真正的去用#来填充原列表,而是在想象中完成,所以我们通过第三个判断来判断在想象中填充过的字符串的两个对应位置是否都是#号,第四个判断条件则是判断想象中填充过的字符串的两个来自于源字符串的字符是否相同; 当然,你也可以真的建立一个填充过的列表,那么第三和第四条判断就变成了:填充过的数组[i-radses[i]] == 填充过的数组[i+radses[i]];但是多用了n+1个空间"""while i - (radses[i] + 1) >= 0 and i + (radses[i] + 1) < enriched_len and \(((i - (radses[i] + 1)) % 2 == 0 and (i + (radses[i] + 1)) % 2 == 0) or(test_str[math.floor((i - (radses[i] + 1))/2)] ==test_str[math.floor((i + (radses[i] + 1))/2)])):radses[i] = radses[i] + 1   # 两端都可以扩展,那么半径扩展1if current_rights_index < i + radses[i]:  # 保持current_rights_index为最右的端点current_rights_index = i + radses[i]current_center = iif result_rads < radses[i]:  # 保存result_rads为半径最大(也就是我们要求的最长回文串)result_rads = radses[i]result_center = i"""
这里用到了前提(3),从填充的字符的开始和结束下标转换为源字符的开始和结束下标解释结束下标应该为
(result_center+result_rads-1)/2向下取整,
由于在python中,为前开后闭,所以变成了
(result_center+result_rads-1+1)/2向下取整,
也就是(result_center+result_rads)/2向下取整,
"""
print(test_str[math.floor((result_center-result_rads) / 2):math.floor((result_center+result_rads)/2)])



时间复杂度(原子操作是比较),下图中的每一个绿色,都代表该方格与它左边的对称方格进行了一次比较:


可以看到,比较的次数不可能是 n^2+(......),而是 an+常数,所以时间复杂度是O(n)




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

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

相关文章

项目管理实战之团队管理 (转自: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}…

python3 正则表达式模块re相关

# -*- coding:utf-8 -*- # Author: Evan Mi import re """ . 默认匹配除\n之外的任意一个字符&#xff0c;若指定flag DOTALL,则匹配任意字符&#xff0c;包括换行 ^ 匹配字符开头&#xff0c;若指定flags MULTILINE,这种也可以匹配上(r"^a",…

pageContext对象

这个对象代表页面上下文&#xff0c;该对象主要用于访问JSP之间的共享数据。使用pageContext可以访问page、request、session、application范围的变量。 pageContext是PageContext类的实例&#xff0c;它提供了如下两个方法来访问page、request、session、application范围的变量…