Manacher详解
之前的字符串题解中对Manacher的思想进行了简略的介绍,在这篇文章中,我将会详细的将这个算法的初衷和具体实现理论进行解释。声明一点,这是我个人的理解,可能有不全面之处,望多包涵。在之前的几篇文章中,我也发现有个别的编辑错误,希望大家在看的时候多加思考,不要被我的思维禁锢。
可能有的人没有看过之前的文章,那么我再赘述几句。
回文串:以最中间一个字符或者中轴线为对称轴左右两侧镜像相等的字符串。
例如 :
123321 这个字符串前三个字符和后三个字符镜像相等
1234321 这个字符串 前四个字符和后四个字符镜像相等。
从上面的例子可以看出,回文串有两种可能,一种是偶对称,即对称轴的位置没有字符,并且整个字符串的字符个数是偶数;另一种是奇对称,即对称轴的位置是一个字符,并且整个字符串的字符个数是奇数。普通的处理方法是将两种情况分开讨论,逐个枚举回文中心,由中心向两边扩散,确定回文串的长度。
Manacher算法就是在这个基础上进行改进的,他的改进主要有两个方面:
1⃣️上述方法需要将情况分为奇对称和偶对称两种情况,比较麻烦,Manacher将原字符串进行了些微的改进,就可以用统一的方法对得到的新字符串进行处理。方法如下:
奇数和偶数之和必定为奇数,那么,在一个字符串的首尾和每两个字符之间加入一个标记符,就可以将所有的偶对称子串处理为奇对称串,这样仅用奇对称判断就可以处理偶对称的问题。
举例如下:
原串 123321
新串 *1*2*3*3*2*1*
将原来的6字符变成了13字符
原串 1234321
新串 *1*2*3*4*3*2*1*
将原来的7字符变成了15字符
如上所示,所有字符巧妙的变成了奇对称。如果原来回文串的字符个数是奇数,那么改进后的回文串的对称中心一定是字母,且该字母一定也是原回文串的对称中心。如果原来回文串的字符个数是偶数,那么改进后的回文串对称中心一定是插入的特殊符号(本文中为*),且该符号的位置在原串中是对称轴的位置。
2⃣️之前的办法是将每个字符作为回文中心来确定以该字符为中心的回文串长度,也就意味着需要无数次的枚举和比较。Manacher 的第二个改进方向就是,利用之前确定的回文串的特征,减少本回文中心所在回文串确定长度时的判断次数。
现在就有两个问题,第一,既然要借助之前求得的回文串特征,那么在这个过程中必然应该将之前求得的所有回文串的信息记录下来,至少应该记录其回文中心和回文串长度吧?Manacher 选择记录的是回文串中心和回文半径(回文串的一半长度),这个过程是用一个叫做 p[]数组的以及它的下标完成的,如p[ i ]就表示以 i 为回文中心的字符串的回文半径为 p[ i ]。第二,之前已经判断过很多个回文串了,到底借助哪个回文串效果最好呢。
如下:
如果现在要判断A位置的回文长度,idx,i,i‘ 这三个中心的回文串长度已经得知,那么应该借助以idx为回文中心的这个回文串还是以 i 为回文中心的回文串,或者是 以 i’ 为中心的回文串?答案是 选择 idx 为中心的回文串,这个时候就有人会说:idx 那个回文串长度最长,覆盖面最广,直觉上来讲,就应该选它。
如果你也这么想,那请看下面这个
这个时候,求 A 点处的回文半径,应该借助哪个回文串呢?如果这时候你的答案还是 idx ,那我可以明确地告诉你:宝贝,这次你真的猜错了。因为这个时候需要借助的是 B 为中心的回文串了。为什么会这样?因为要求 A 所在的回文串长度,必然得借助和它关系最大的一个回文串,这样才能尽可能保证借助到的长度最多,正如上图所展示,B 比 idx 的右边界更远一些,也就意味着,B 对“前方的道路”更了解一些,尽管idx确实在长度上占优势,在A的左侧 idx所在回文串长度远远长于B所在回文串,但在A点的右侧,B的长度更多一些。回文中心左右两侧元素个数是相等的,即模版回文串分布在A点左右两侧可以用得到的字符个数是相等的(以最少的一方计算),从这个角度来看,选B更合理一些。由此可以得出一个结论:求当前中心所在回文串长度,要借助的模版必定是已经求得的回文串中右边界最远的回文串。
知道了需要借助哪个模版串之后,接下来就需要讨论如何借助模版串的问题。想知道如何借助模版串,首先应该清楚的是模版串和当前要求的回文中心的位置关系,这样才好下手。
模版串和当前回文中心的位置关系共有四种可能:
在第一种情况中,选择的模版串中心是 idx,其右边界为 idx右 ,i‘ 是 i 关于 idx 对称的点,由于idx是回文串,因此,在 idx串 内部 i 和 i’ 的对称情况是一样的,可以跳过这部分比较提高效率,但是在 idx 串之外i‘ 的情况 和 i 的情况是否一样则无法确定,因此需要在 idx右边界之外 对 i 的对称情况进行确定。这种情况中 i 串 最短的可能长度是 idx右-i ,在此基础上对 idx右的右侧和 i -(idx右 - i )的左侧进行处理即可。
在第二种情况中,选择的模版串 idx ,其 右边界 记为 idx右,i’ 是 i 关于 idx 的对称点,同上,在 idx 内部,i‘ 和 i 的对称情况一样,因此在上图这种情况下, i 串 的回文半径至少等于 i’ 的回文半径,但在idx右的右侧,是否还有字符使 i 回文不可知,因此 在i'回文长度的基础上,对 idx右 的右侧和 i - ( idx右 - i )的左侧进行处理即可。
3.
在第三种情况中,选择的模版串 idx ,其右边界记为 idx右, i‘ 是 i 关于 idx 的对称点,同上,在 idx 内部,i’ 和 i 的对称情况是一样的,又因为 i 和 i‘ 串完全被 idx 串包含,因此对称情况完全一样,即 i 串 回文长度 最终等于 i' 串的长度
4.
在第四种情况中,i 串 完全脱离之前已经求得的所有回文串,因此只能以 i 为中心在其左右两侧逐个判断。
以上是理论知识的介绍,下面我将具体实现代码给大家进行解读,要说一点,代码的实现和理论知识略有不同,代码的实现将上述情况进行了综合,把其中相同的操作进行合并。
主要声明:
const int maxn; // 存放字符串长度可能的最大值
char str[ 3 * maxn ] , //存放处理后的新字符串
s[ maxn ] ; //存放原串
int p[ 3 * maxn ];存放新串每个字符对应的回文半径长度
int len1, //存放原串的长度;
len2 ; //存放处理后新串的长度-1;
Manacher的代码实现分为两个部分。
1⃣️第一部分 生成新串
2⃣️第二部分 生成p[]数组
代码很精简,但逻辑性特别强,方法也很独特。今天的分享到此结束,感谢您的阅读。
Manacher详解的更多相关文章
- 算法进阶面试题01——KMP算法详解、输出含两次原子串的最短串、判断T1是否包含T2子树、Manacher算法详解、使字符串成为最短回文串
1.KMP算法详解与应用 子序列:可以连续可以不连续. 子数组/串:要连续 暴力方法:逐个位置比对. KMP:让前面的,指导后面. 概念建设: d的最长前缀与最长后缀的匹配长度为3.(前缀不能到最后一 ...
- Linq之旅:Linq入门详解(Linq to Objects)
示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...
- 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)
一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...
- EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用详解
前言 我比较喜欢安静,大概和我喜欢研究和琢磨技术原因相关吧,刚好到了元旦节,这几天可以好好学习下EF Core,同时在项目当中用到EF Core,借此机会给予比较深入的理解,这里我们只讲解和EF 6. ...
- Java 字符串格式化详解
Java 字符串格式化详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 文中如有纰漏,欢迎大家留言指出. 在 Java 的 String 类中,可以使用 format() 方法 ...
- Android Notification 详解(一)——基本操作
Android Notification 详解(一)--基本操作 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Notification 文中如有纰 ...
- Android Notification 详解——基本操作
Android Notification 详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 前几天项目中有用到 Android 通知相关的内容,索性把 Android Notificatio ...
- Git初探--笔记整理和Git命令详解
几个重要的概念 首先先明确几个概念: WorkPlace : 工作区 Index: 暂存区 Repository: 本地仓库/版本库 Remote: 远程仓库 当在Remote(如Github)上面c ...
- Drawable实战解析:Android XML shape 标签使用详解(apk瘦身,减少内存好帮手)
Android XML shape 标签使用详解 一个android开发者肯定懂得使用 xml 定义一个 Drawable,比如定义一个 rect 或者 circle 作为一个 View 的背景. ...
随机推荐
- 利用mvc filterconfig属性实现权限验证
好久没写过博客了,突然发现最后博客更新时间是2016年,感觉好长远 权限控制基本是所有cms系统或者进销存,或者几乎所有能和业务系统扯上关系的系统都要用上的一个模块,很多都想把这个模块独立出来,权限单 ...
- MySql COUNT(),SUM()组合用法
需求:统计工作室表的数量,按创建时间分组 问题:按创建时间分组,如何将计数后的结果求和 解决:用COUNT(DISTINCT col_name)讲每行计数为1,再求和分组 SELECT DATE_FO ...
- 2017多校第10场 HDU 6171 Admiral 双向BFS或者A*搜索
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6171 题意: 给你一个高度为6的塔形数组,你每次只能将0与他上下相邻的某个数交换,问最少交换多少次可以 ...
- BZOJ 3379: [Usaco2004 Open]Turning in Homework 交作业
Description 贝茜有C(1≤C≤1000)门科目的作业要上交,之后她要去坐巴士和奶牛同学回家. 每门科目的老师所在的教室排列在一条长为H(1≤H≤1000)的走廊上,他们只在课后接收 ...
- Docker 集群环境实现的新方式
近几年来,Docker 作为一个开源的应用容器引擎,深受广大开发者的欢迎.随着 Docker 生态圈的不断建设,应用领域越来越广.云计算,大数据,移动技术的快速发展,加之企业业务需求的不断变化,紧随技 ...
- js文件引用方式及其同步执行与异步执行
详见: http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp74 任何以appendChild(scriptNode) 的方式引入 ...
- 关于javascript原型链的个人理解
首先js是一种面对对象的语言,虽然大多数时候是以面对过程的形式展现出来.先来看一段代码: function Base() { this.name = 'tarol'; } function Sub() ...
- 201521044091《Java程序设计》第7周学习总结
1. 本周学习总结 ArrayList代码分析 1.1 解释ArrayList的contains源代码 用于判断Collection中是否包含某个元素.List<T>的contains方法 ...
- 201521123109《java程序设计》第七周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合相关内容. 2. 书面作业 ArrayList代码分析 1.1 解释ArrayList的contains源代码 源代码: pub ...
- 201521123037 《Java程序设计》第5周学习总结
1. 本周学习总结 1.1 尝试使用思维导图总结有关多态与接口的知识点. 1.2 可选:使用常规方法总结其他上课内容. 接口: 接口简而言之是方法声明和常量值的集合,接口中所有的方法默认为public ...