之前的字符串题解中对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更合理一些。由此可以得出一个结论:求当前中心所在回文串长度,要借助的模版必定是已经求得的回文串中右边界最远的回文串。

知道了需要借助哪个模版串之后,接下来就需要讨论如何借助模版串的问题。想知道如何借助模版串,首先应该清楚的是模版串和当前要求的回文中心的位置关系,这样才好下手。

模版串和当前回文中心的位置关系共有四种可能:

  1. 在第一种情况中,选择的模版串中心是 idx,其右边界为 idx右 ,i‘ 是 i 关于 idx 对称的点,由于idx是回文串,因此,在 idx串 内部 i 和 i’ 的对称情况是一样的,可以跳过这部分比较提高效率,但是在 idx 串之外i‘ 的情况 和 i 的情况是否一样则无法确定,因此需要在 idx右边界之外 对 i 的对称情况进行确定。这种情况中 i 串 最短的可能长度是 idx右-i ,在此基础上对 idx右的右侧和 i -(idx右 - i )的左侧进行处理即可。

  2. 在第二种情况中,选择的模版串 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详解的更多相关文章

  1. 算法进阶面试题01——KMP算法详解、输出含两次原子串的最短串、判断T1是否包含T2子树、Manacher算法详解、使字符串成为最短回文串

    1.KMP算法详解与应用 子序列:可以连续可以不连续. 子数组/串:要连续 暴力方法:逐个位置比对. KMP:让前面的,指导后面. 概念建设: d的最长前缀与最长后缀的匹配长度为3.(前缀不能到最后一 ...

  2. Linq之旅:Linq入门详解(Linq to Objects)

    示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...

  3. 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)

    一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...

  4. EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用详解

    前言 我比较喜欢安静,大概和我喜欢研究和琢磨技术原因相关吧,刚好到了元旦节,这几天可以好好学习下EF Core,同时在项目当中用到EF Core,借此机会给予比较深入的理解,这里我们只讲解和EF 6. ...

  5. Java 字符串格式化详解

    Java 字符串格式化详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 文中如有纰漏,欢迎大家留言指出. 在 Java 的 String 类中,可以使用 format() 方法 ...

  6. Android Notification 详解(一)——基本操作

    Android Notification 详解(一)--基本操作 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Notification 文中如有纰 ...

  7. Android Notification 详解——基本操作

    Android Notification 详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 前几天项目中有用到 Android 通知相关的内容,索性把 Android Notificatio ...

  8. Git初探--笔记整理和Git命令详解

    几个重要的概念 首先先明确几个概念: WorkPlace : 工作区 Index: 暂存区 Repository: 本地仓库/版本库 Remote: 远程仓库 当在Remote(如Github)上面c ...

  9. Drawable实战解析:Android XML shape 标签使用详解(apk瘦身,减少内存好帮手)

    Android XML shape 标签使用详解   一个android开发者肯定懂得使用 xml 定义一个 Drawable,比如定义一个 rect 或者 circle 作为一个 View 的背景. ...

随机推荐

  1. 【Spring 核心】AOP 面向切面编程

    一.什么是面向切面编程? 二.通过切点来选择连接点 三.使用注解创建切面 四.在XML中声明切面 五.注入AspectJ切面

  2. Win7下C:\Users\Cortana以账户名称命名的系统文件夹用户名的修改

    Win7下C:\Users\Cortana以账户名称命名的系统文件夹用户名的修改 Win7下C:\Users\Cortana以账户名称命名的系统文件夹用户名的修改 即修改Cmd命令提示符:C:\Use ...

  3. 关于request、response转发与重定向的简述

    在做页面的请求与响应的时候我们多用request与response进行操作,而我们大家也知道,request是表示用户发向服务器的请求,而response是对用户请求的一个响应. 关于转发和重定向,通 ...

  4. QQ无法通过ISA2006&TMG2010代理收发图片问题解决

    近期公司有业务需求通过TMG访问QQ,但配置多次均无法通过QQ收发图片,文字输入正常. 目前已解决,供参考: 这个问题是SSL端口默认使用了443,但QQ的离线文件不使用这个端口.所以ISA会把QQ的 ...

  5. windows平台下node,npm,gulp配置

    参考文献:http://blog.csdn.net/yuanyuan214365/article/details/53749583 1.安装nodejs:nodejs nodejs安装路径随意 nod ...

  6. 用PyCharm执行测试成功但无法生成HTMLTestRunner报告

    问题:代码写的没问题,执行也成功了,但就是无法生成HTMLTestRunner的报告 其实这是编辑器搞得鬼,编辑器为了方便用户执行测试,都有一项功能,可以用编辑器来调用unittest或者nose来执 ...

  7. POJ3228 并查集或二分最大流枚举答案

    忘记写题意了.这题题意:给出每个地点的金矿与金库的数量,再给出边的长度.求取最大可通过边长的最小权值使每个金矿都能运输到金库里. 这题和之前做的两道二分枚举最大流答案的问法很相识,但是这里用最大流速度 ...

  8. 浅谈一下Java String

    相信很多同学使用Java String, Java中的String方法,但是对其中的原理可能有些模糊,那么咱们就针对这块内容进行展开,让更多的同学理解和知道. public final class S ...

  9. 团队作业8——第二次项目冲刺(Beta版本)5.24

    1.当天站立式会议照片 会议内容 1.总结前几次会议中出现的问题. 2.对第二天需要做的任务进行分配. 3.询问团队队员任务完成情况以及时间分配是否充分. 4.对今后的任务,发表自己的看法. 2.每个 ...

  10. Sudoku Generator

    Sudoku 算法 标签(空格分隔): 软工实践 设想:通过第一行,来生成2, 3行的排列,再通过1 - 3 生成4 - 6排列. 2 3 行的生成由上一行生成 公式为$grid[i][j] = gr ...