Longest Palindromic Substring Part II

Longest Palindromic Substring Part II

给定一个字符串 S, 找出字符串S中的最长回文子字符串.

注意:

这是关于:Longest Palindromic Substring.的第二篇文章了.在这里,我们讨论一种可以再线性时间内找到字符串中最长回文子字符串的一种算法(Manacher’s algorithm).请先阅读第一篇文章(Part I )作为背景知识.

在之前的的文章里面我们已经讨论了四种不同的方法,那些方法中最简单的方法的时间复杂度为O(N2),空间复杂度为常数.在这里,我们将讨论一种时间复杂度为O(N)空间复杂度也为O(N)的一种算法,名字叫做 Manacher’s algorithm.

考虑一下如何提高之前提到的那种时间复杂度为O(N2)的方法,先考虑一下最坏情况.最坏情况便是输入是一个全是重复字符的字符串.比如 "aaaaaaa" 和"cavcavcvcavcav".实际上,我们可以利用回文字符串的对称性来避免一些不必要的计算.

一个O(N)的解法(Manacher’s Algorithm):

首先,我们通过在输入的字符串S的每个字符之间插入字符'#'将字符串S转化成字符串T.为什么这么做呢?一会儿你就明白了.

例如: S = “abaaba”, T = “#a#b#a#a#b#a#”.

为了找到最长的文字子序列,我们需要从Ti 向两边扩展,找到一个Ti-d … Ti+d 是一个回文序列.你马上就会明白其实d就是以Ti 为中点的回文序列的长度.

We store intermediate result in an array P, where P[ i ] equals to the length of the palindrome centers at Ti. The longest palindromic substring would then be the maximum element in P.

我们将得到的结果保存在一个数组P中,其中P[i]的值就是Ti 为中点的回文序列的长度.那么最长的回文子序列就是数组P中的最大元素了.

基于上边的例子,我们可以得到对应的P:

T = # a # b # a # a # b # a #
P = 0 1 0 3 0 1 6 1 0 3 0 1 0

观察数组P,我们会发现“abaaba”的最长回文子序列长度是P6 = 6.

不知道你是否注意到,通过在字符串中插入字符'#',我们可以很容易的处理奇数和偶数的回文序列.

现在,想象一下你在会回文序列“abaaba”的中间画了一条垂线.你是否注意到P中元素的值是关于这条线是对称的.这不是个例.对于另外一个回文"aba",依然有这样的特点.通过这个特性,我们可以简化求P[i]中元素值的过程.

让我们再看一个稍微复杂一点儿的例子吧.S = “babcbabcbaccba”


上面的T字符串是由S转化而来.假定你已经求得了P中一部分值,(也就是P中?之前的值你已经求得)实线代表“abcbabcba”的中心.两条虚线分别代表左(L)右(R)边界.你现在处在i的位置,关于C与i对称的是 i’.那么你将如何计算P[i]的值呢?

这里我们假定你已经计算到了i = 13的位置,我们现在需要计算P[13](对应于图中?的位置).我们首先看一下他的对称点 i’ . i’  = 9;


图中绿线表示的是i 和 i’所覆盖的区域.i关于C的对称点 i’,P[ i’ ] = P[ 9 ] = 1.这很明显,P[i]肯定等于1.因为关于C他们是对称的.

正如你在上面所看见的, 由于回文序列关于中点对称的性质,我们可以很容易的得知P[ i ] = P[ i’ ] = 1.事实上,接下来的三个元素的值也可以同样得到( P[ 12 ] = P[ 10 ] = 0, P[ 13 ] = P[ 9 ] = 1, P[ 14 ] = P[ 8 ] = 0)


现在我们计算到了i= 15的位置,I的对应点 i’ = 7,P[15]是否也等于7呢?

现在我们计算到了i= 15的位置,P[i]的值到底是多少呢?如果我们继续使用对称来做,那么它的值还是等于P[7] = 7.但是那是错的.如果我们以 T15,为中心向两边扩展,会形成回文序列“a#b#c#b#a”,但是它的长度却不是7而是小于7.为什么呢?


Colored lines are overlaid around the center at index i and i’. Solid green lines show the region that must match for both sides due to symmetric property around C. Solid red lines show the region that might not match for both sides. Dotted green lines show the region that crosses over the center.分别以 i 和i’为中点时,它们覆盖区域是重叠的.绿色实线表示关于C对称的两部分所覆盖的区域.红色实线表示的是不对称的区域.绿色虚线表示跨越C的区域.

通过上图可以很清楚地看出两条绿色实线对应的的区域是完全匹配的.跨越中点的区域(绿色虚线对应区域)也是对称的.仔细看,P[ i ‘ ]是7,它扩展的区域已经超过了回文序列的左边界(L,红线表示).而那一部分已经不属于对称的回文序列中了.我们可以确定的是 P[ i ] ≥ 5.为了找到 P[ i ] 的实际值,我们需要一个一个字符的匹配扩展到右边界之外(R,红线表示).在这个示例中,最后结果是 P[ i ] ≥ 5.

让我们总结一下算法的主要部分:

if P[ i’ ] ≤ R – i,
then P[ i ] ← P[ i’ ]
else P[ i ] ≥ P[ i’ ]. (Which we have to expand past the right edge (R) to find P[ i ].

是不是很简单呢?如果你已经掌握了它,那么已经掌握了这个算法的精华,同时也是最难得部分.

最后要做的是就是我们应该在什么时候将C和R一起向右移动呢,这是非常简单的:

If the palindrome centered at i does expand past R, we update C to i, (the center of this new palindrome), and extend R to the new palindrome’s right edge.

对于每一步,有两种可能.如果P[ i ] ≤ R – i, 我们将P[ i ] 赋值为 P[ i’ ].此步骤只需要花费1布.否则我们尝试着将i作为中点,扩展这个新回文序列,并且是以右边界(R)所在位置为起点.这个步骤最多花费N布,定位和测试每个中点需要花费N布,因此这个算法可以确保在2*N布之内给出结果.

// Transform S into T.
// For example, S = "abba", T = "^#a#b#b#a#$".
// ^ and $ signs are sentinels appended to each end to avoid bounds checking
stringpreProcess(strings){intn=s.length();if(n==0)return"^$";stringret="^";for(inti=0;i<n;i++)ret+="#"+s.substr(i,1);ret+="#$";returnret;
}stringlongestPalindrome(strings){stringT=preProcess(s);intn=T.length();int*P=newint[n];intC=0,R=0;for(inti=1;i<n-1;i++){inti_mirror=2*C-i;// equals to i' = C - (i-C)P[i]=(R>i)?min(R-i,P[i_mirror]):0;// Attempt to expand palindrome centered at iwhile(T[i+1+P[i]]==T[i-1-P[i]])P[i]++;// If palindrome centered at i expand past R,// adjust center based on expanded palindrome.if(i+P[i]>R){C=i;R=i+P[i];}}// Find the maximum element in P.intmaxLen=0;intcenterIndex=0;for(inti=1;i<n-1;i++){if(P[i]>maxLen){maxLen=P[i];centerIndex=i;}}delete[]P;returns.substr((centerIndex-1-maxLen)/2,maxLen);
}


原文地址: Longest Palindromic Substring Part II

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

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

相关文章

Java反射基础(一)--Class对象获取

Classes Java中,任何一个对象要么是一个引用类型要么是基本数据类型.引用类型指的是那些直接或间接 Java.lang.Object的类.Classse,enum,和接口都是应用类型.基本类型是一个固定的集合,它包括: boolean, byte,short, int, long,char,float, double. java.lang.String和所有的基…

Java反射基础(二)--Fileds对象的使用

在说Filed之前,我们先来了解一下Member接口. 反射中定义了一个接口 java.lang.reflect.Member . java.lang.reflect.Field, java.lang.reflect.Method, 和java.lang.reflect.Constructor 都实现了该接口.我们将在接下来的部分介绍这些类.对于每个Member, 我们都会介绍相关的API…

Java反射基础(三)--Methods对象的使用

Method 原文地址:http://docs.oracle.com/javase/tutorial/reflect/member/method.html 1.获得方法类型信息 一个方法的声明包括方法名,修饰符, 参数, 和返回类型,同时还有一些方法可能抛出的异常. 类 java.lang.reflect.Method 提供了一种方式让我们可以得到方法的这些信息. …

Guice基本用法

本文适合对依赖注入有相对了解的读者&#xff0c;文章中对于部分名词未作详细解释。对于没有恰当的中文与之对应的英文内容&#xff0c;遂未翻译 Guice简介 Guice 简介&#xff0c;本文中的内容也是参考该文档完成&#xff0c;如有不一致&#xff0c;以该文为准。 快速上手 …

Guice之Servlet基础

如果读者对于Guice没有大体的了解&#xff0c;可以参考本人的另一篇Guice基础文章 Guice 提供了一个完整的体系使得我们在web应用中也可以使用它作为依赖注入的工具. 为什么使用 Guice : 使用Guice的好处: 构造函数注入类型安全的, 方便的配置方式(只需要在web.xml中进行很…

矩阵 I : 矩阵基础

学习机器学习, 基础的线性代数知识是必备的基础功, 对于线性代数的探索, 矩阵是线性代数的主要研究对象. 今天我们就开始学习一下矩阵的基础知识. 这是本人关于线性代数矩阵的第一篇分享. 章节目录 矩阵及其基本运算 1.1 矩阵定义 1.2 矩阵基本运算(,-,*) 1.3 转置矩阵 1…

矩阵 II : 线性组的线性相关性

学习机器学习, 基础的线性代数知识是必备的基础功, 对于线性代数的探索, 向量组也是线性代数的重要基础. 今天我们就开始学习一下线性代数中重要的向量组知识. 这是本人关于线性组的线性相关性的学习分享. 章节目录 相关性基本概念 1.1 相性相关和线性无关 1.2 相性相关性的…

汇编语言笔记(一):基础

章节目录 简单程序 使用段简单字符串处理程序使用 bx, si, di, bp 寄存器寻址寻址方法指明数据长度div指令 作者能力有限, 如果您在阅读过程中发现任何错误, 还请您务必联系本人,指出错误, 避免后来读者再学习错误的知识.谢谢! 本文中所有程序均在DOSBox下使用MASM, LINK编译运…

汇编学习笔记(二):转移指令

章节目录 转移指令原理 jmp 指令 jcxz 指令 loop 指令 ret 和 retf 指令 call 指令 callret 作者能力有限, 如果您在阅读过程中发现任何错误, 还请您务必联系本人,指出错误, 避免后来读者再学习错误的知识.谢谢! 本文中所有程序均在DOSBox下使用MASM, LINK编译运行 转移指令…

汇编语言笔记(三): 标志寄存器

章节目录 简介 ZF 标志寄存器PF 标志寄存器SF 标志寄存器CF 标志寄存器OF 标志寄存器几条相关指令DF 标志寄存器PUSHF and POPF 标志寄存器 作者能力有限, 如果您在阅读过程中发现任何错误, 还请您务必联系本人,指出错误, 避免后来读者再学习错误的知识.谢谢! 简介 8086 CPU…

汇编语言笔记(四):内中断

汇编语言笔记:内中断 章节目录 概念 中断过程示例: 0 号中断处理 作者能力有限, 如果您在阅读过程中发现任何错误, 还请您务必联系本人,指出错误, 避免后来读者再学习错误的知识.谢谢! 概念 中断信息: 任何一个通用 CPU 都具备一种能力, 可以在执行完当前正在执行的指令之…

sync.Map 源码学习

golang 线程安全的 Map 作者水平有限,而并发博大精深. 如文章中有任何错误, 希望读者不吝指出.谢谢! 章节目录 Map 基本类型定义StoreLoadDeleteRange Map 基本类型定义## Map 这里我们先看一下 Map 都涉及到那些类型. type Map struct {// 互斥锁. 用于互斥的读写 dirty.…

ASN.1 学习

ASN.1 章节目录 简介常用数据类型 2.1 常见的简单类型 2.2 结构类型Basic Encoding RulesDistinguished Encoding Rules编码示例 5.1 BIT STRING 5.2 IA5String 5.3 INTEGER 5.4 NULL 5.5 OCTET STRING 5.6 UTCTime 5.6 OBJECT IDENTIFIER编码 Name (X.501 type) 参考 http://…

证书体系: PFX 文件格式解析

原文同时发布于本人个人博客&#xff1a; https://kutank.com/blog/cert-pfx/ 章节目录 PFX 简介PFX 格式解析 2.1 最外层结构 2.2 AuthenticatedSafe 结构 参考 https://tools.ietf.org/html/rfc7292. PFX 简介## 以下引用自维基百科 在密码学中&#xff0c;PKCS #12 定义了…

C10K 非阻塞 Web 服务器

本文由作为 Going Concurrency in Go 的作者 Nathan Kozyra 撰写, 解决了互联网上最著名,最受尊敬的挑战之一, 并试图通过核心 Go 包来解决它. 原文地址: https://hub.packtpub.com/c10k-non-blocking-web-server-go/ 我们已经构建了一些可用的应用程序,并且可以在日常使用的真…

MD5 算法描述及实现

MD5 算法的原理及实现 章节目录 简介算法描述 实现 作者能力有限, 如果您在阅读过程中发现任何错误, 还请您务必联系本人,指出错误, 避免后来读者再学习错误的知识.谢谢! 简介## Wiki对其的描述: MD5消息摘要算法&#xff08;英语&#xff1a;MD5 Message-Digest Algorithm&…

SHA 算法描述与实现

SHA 算法的原理及实现 章节目录 简介算法描述 2.1 数据准备 2.1.1 数据填充 2.1.2 数据分块 2.1.3 设置初始 Hash 值 2.2 Hash 计算 2.2.1 SHA-1 2.2.2 SHA-256 2.2.3 SHA-512实现 作者能力有限, 如果您在阅读过程中发现任何错误, 还请您务必联系本人,指出错误, 避免后来读者…

SHA算法描述及实现

SHA 算法的原理及实现 章节目录 简介算法描述 2.1 数据准备 2.1.1 <数据填充 2.1.2 数据分块 2.1.3 设置初始 Hash 值 2.2 Hash 计算 2.2.1 SHA-1 2.2.2 SHA-256 2.2.3 SHA-512实现<b>作者能力有限, 如果您在阅读过程中发现任何错误, 还请您务必联系本人,指出错误, …

CNG 关于 Key 相关的操作

章节目录 简介创建 Key查看系统中的 Key从 Windows Store 导出 key导入 Key 到 Windows Store<b>作者能力有限, 如果您在阅读过程中发现任何错误, 还请您务必联系本人,指出错误, 避免后来读者再学习错误的知识.谢谢!</b> 简介 CNG 全称 Cryptography API: Next G…

Golang 词法分析器浅析

浅析 Go 语言的词法分析器 章节目录 简介TokenScanner例子 作者能力有限, 如果您在阅读过程中发现任何错误, 还请您务必联系本人,指出错误, 避免后来读者再学习错误的知识.谢谢! 简介## 在本文我们将简单的走读 Go 语言的词法分析器实现(go/scanner/scanner.go). 本文基于 G…