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 的背景. ...
随机推荐
- 微信小程序--ajax服务器交互及页面渲染
网上找的帖子大多是直接在onload中请求数据.而我想实现的是点击按钮,然后请求服务器,接着返回数据,前端页面渲染.所以搞了挺久的,在此记录一下. 请求是按照微信官方给出的,wx.request 在这 ...
- SpringMVC基础-controller方法中的参数注解
@PathVariable 映射 URL 绑定的占位符 带占位符的 URL 是 Spring3.0 新增的功能,该功能在 SpringMVC 向 REST 目标挺进发展过程中具有里程碑的意义 通过 ...
- Nexus私服搭建使用及发布jar包到私服上供团队其他成员使用
1.下载maven解压到指定目录,并配置环境变量 M2_HOME为maven解压目录 2.path中增加 %M2_HOME%\bin, 并确认 mvn -v 正确 3.下载nexus http://w ...
- 使用idea和studio进行调试的方法
新入职一个公司,使用得IDE发生了一些变化 ,对于idea的使用,之前有提到过,今天主要的内容是使用idea和studio进行调试的快捷键. 虽然现在计算机开发的语言多种多样,但是使用C#写客户端,使 ...
- MySQL(一)之MySQL简介与安装
大家可能都在用MySQL,其实我也是在用MySQL的,但是你知道吗?大部分人都是在windows中使用,这里将介绍一下在windows中的安装分为安装包安装与MSI包安装,以及在linux中的在线安装 ...
- TC358749XBG:HDMI转MIPI CSI芯片简介
TC358749XBG是一颗HDMI转MIPI CSI功能的视频转换芯片,分辨率:1920*1080,电源3.3/1.8/1.2,通信方式:IIC,封装形式BGA80
- Mysql分页处理(PageHelper)
第一次做分页处理(完全不知道分页处理到底要做成什么样?) 理解:其实就是前台页面通过传递不同的参数{1.查询的条件.2.查询页数(pageNum),3每页展现的条数(pageSize)},之后我们(只 ...
- TP 3.2 笔记 (1)
1.配置文件分布在好多子模块中 2.I方法 使用指定过滤方法来过滤变量,第三个参数如果是函数名,则会调用该函数进行过滤,(在变量是数组的情况下自动使用array_map进行过滤处理),否则会调用 PH ...
- 转:【Java并发编程】之一:可重入内置锁
每个Java对象都可以用做一个实现同步的锁,这些锁被称为内置锁或监视器锁.线程在进入同步代码块之前会自动获取锁,并且在退出同步代码块时会自动释放锁.获得内置锁的唯一途径就是进入由这个锁保护的同步代码块 ...
- JS学习五(js中的事件)
[JS中的事件分类] 1.鼠标事件 click/bdlclick/onmouseover/onmouseout 2. HTML事件 onload/onscroll/onsubmit/onchange/ ...