Manacher's algorithm
Manacher's algorithm 以\(O(n)\)的线性时间求一个字符串的最大回文子串。
1. 预处理
一个最棘手的问题是需要考虑最长回文子串的长度为奇数和偶数的情况。我们通过在任意两个字符之间填充 #
的方法, 将原字符串 \(S\) 转化为辅助字符串 \(T\),具体例子如下:
S = a b a a b a
T = # a # b # a # a # b # a #
转化后便可不必再考虑奇偶问题,同时辅助字符串的长度也变为奇数。
转化后字符串\(T\)的长度为奇数:
在长度为奇数的字符串之间(包括外侧),有偶数个位置;在长度为偶数的字符串之间(包括外侧) ,有奇数个位置,所有这样处理之后,字符串的长度都会变为奇数。事实上,公式\(2 \times len + 1\) 已经说明预处理之后的字符串长度必为奇数。
奇回文串和偶回文串一起处理:
以字符 #
为中点位置,处理的就是偶回文串的情况,
以其他字符为中点位置,处理的就是奇回文串的情况。
为了避免出现数组访问越界的边界问题,我们将字符串\(T\)的首部再添加一个原字符串\(S\)中没有出现的字符,最后处理完的字符串如下:
S = a b a a b a
T = # a # b # a # a # b # a #
T = $ # a # b # a # a # b # a #
这样,预处理工作完成,下面进入manacher算法的核心部分。
2. manacher's algorithm
这里,定义一个数组\(p[]\) 和 两个变量 \(r\) 和 \(c\)。
\(p[i]\)表示以位置\(i\)为中点的最大回文子串的长度。
\(r\) 表示当前所有检测过的位置所能到达的最右端。
\(c\) 为与 \(r\) 对应的 \(i\) 的位置,与 \(r\) 同时更新,实际上 \(c+p[i]=r\) 。
回忆下一个\(O(n^2)\)的做法:
从左到右对字符串进行扫描,以每个位置为中点,向两边扩张,并记录最大长度和相应的位置(对于偶数,类似的再处理一遍即可)。
这种算法的空间复杂度为\(O(1)\),是很优秀的,但是,对于每一个位置,都从长度为0开始向两边扩展,这是导致时间复杂度高的一个最主要的原因。
而manacher算法则是额外使用一个\(p[]\)数组记录最大回文子串的长度,
因存在对称关系,数组\(p[]\)的值能够被充分利用,部分\(p[i]\)的值可以在\(O(1)\)的时间确定。
从而使得算法的复杂度降为\(O(n)\)。
这种思路类似于KMP算法,充分利用前面已经匹配过的有用信息。
如何计算数组\(p[]\)的值呢? 我们分两种情况:
\({i}'\) 代表 \(i\) 关于中心 \(c\) 的对称点,计算公式为 \({i}'=2 \times c - i\)
& \text{ if } (i < r ) \;
\begin{cases}
\text{ if } (r-i>p[{i}']) \; \text{then}\; p[i]=p[{i}'] \\
\text{ otherwise } p[i]= r-i \\
\end{cases} \\ \\
& \text{ otherwise } p[i]= 0
\end{cases}
\]
至于为什么是两种情况,请看下面的参考文献,这里图我就不摆了。
这样我们可以轻松得到P数组的值
T = $ # a # b # a # a # b # a #
P = 0 0 1 0 3 0 1 6 1 0 3 0 1 0
容易看出,\(p[7] = 6\)是数组\(p[]\)中的的最大值,这正是原字符串\(S\)的最长回文子串的长度。这样,在线性时间处理完\(P[]\)数组之后,最大回文子串就找到,若还需要输出字符串,那只需要再做一些细节处理即可。
3. 核心代码
void manacher(char* s)
{
int c = 0, r = 0, len = strlen(s);
p[0] = 0;
for( int i = 1; i < len ; ++i ) {
if( r > i ) p[i] = min( p[ 2 * c - i ], r - i );
else p[i] = 0;
while( i < len && s[i + 1 + p[i]] == s[i - 1 - p[i]] ) p[i]++;
if( i + p[i] > r ) {
r = i + p[i];
c = i;
}
}
}
4. 算法复杂度
从代码可以看出。
\(\text{manacher}\)算法只需要线性扫描一遍预处理后的字符串。
对\(p[]\)数组的处理
- \(\text{ if } (i < r ) \;\;O(1)\) 时间可以确定
- \(\text{ otherwise } O(n)\)匹配,但是在情况2下,每次扫描都是从\(r+1\)开始的,且\(r\)自身的变化情况是单调递增的,这样可以保证,字符串T中的每个字符最多被访问2次,所以,该算法的时间复杂度是线性\(O(n)\),事实上,\(\text{while}\)循环执行的总次数是线性次的。
5. 参考文献
Manacher's algorithm: 最长回文子串算法
Longest Palindromic Substring Part II
Manacher's ALGORITHM:
练习见 原博客
Manacher's algorithm的更多相关文章
- Manacher's Algorithm 马拉车算法
这个马拉车算法Manacher‘s Algorithm是用来查找一个字符串的最长回文子串的线性方法,由一个叫Manacher的人在1975年发明的,这个方法的最大贡献是在于将时间复杂度提升到了线性,这 ...
- Manacher’s Algorithm (神啊)
(转载自)http://blog.csdn.net/hopeztm/article/details/7932245 这里描述了一个叫Manacher’s Algorithm的算法. 算法首先将输入字符 ...
- 字符串的最长回文串:Manacher’s Algorithm
题目链接:Longest Palindromic Substring 1. 问题描述 Given a string S, find the longest palindromic substring ...
- Manacher's Algorithm(马拉车算法)
## 背景 该算法用于求字符串的最长回文子串长度. ## 参考文章 >[最长回文子串——Manacher 算法](https://segmentfault.com/a/1190000003914 ...
- 最长子回文字符串(Manacher’s Algorithm)
# # 大佬博客: https://www.cnblogs.com/z360/p/6375514.html https://blog.csdn.net/zuanfengxiao/article/det ...
- 什么是马拉车算法(Manacher's Algorithm)?
提出问题 最长回文子串问题:给定一个字符串,求它的最长回文子串长度. 如果一个字符串正着读和反着读是一样的,那它就是回文串.如a.aa.aba.abba等. 暴力解法 简单粗暴:找到字符串的所有子串, ...
- 【算法总结】Manacher's Algorithm
Manacher's Algorithm针对的是最长回文子串问题.对于此问题,最直接的方法是遍历每一个元素,遍历过程中以每一个字符为中心向两边扩展以寻找此字符为中心的最长回文子串.复杂度O(n2).M ...
- Manacher's Algorithm 马拉车算法(最长回文串)
这个马拉车算法Manacher‘s Algorithm是用来查找一个字符串的最长回文子串的线性方法,由一个叫Manacher的人在1975年发明的,这个方法的最大贡献是在于将时间复杂度提升到了线性,这 ...
- 马拉车算法(Manacher's Algorithm)
这是悦乐书的第343次更新,第367篇原创 Manacher's Algorithm,中文名叫马拉车算法,是一位名叫Manacher的人在1975年提出的一种算法,解决的问题是求最长回文子串,神奇之处 ...
随机推荐
- win10下装Ubuntu双系统
本机状况: ssd在笔记本原来的硬盘位,已经安装win10 机械硬盘在光驱位 现在安装Ubuntukylin16到光驱位的机械硬盘建立双系统,并用Windows管理启动 1,将下载的Ubuntu镜像用 ...
- jquery自定义对话框alert、confirm和prompt
jQuery Alert Dialogs,又一个基于jQuery的提示框插件,主要包括Alert.Confirm.prompt这三种,还有一个高级范例,可以在提示框内嵌入HTML语言,可以自定义风格样 ...
- MySQL基础(非常全)
MySQL基础 一.MySQL概述 1.什么是数据库 ? 答:数据的仓库,如:在ATM的示例中我们创建了一个 db 目录,称其为数据库 2.什么是 MySQL.Oracle.SQLite.Access ...
- Linux vi/vim(转载)
Linux vi/vim 所有的 Unix Like 系统都会内建 vi 文书编辑器,其他的文书编辑器则不一定会存在. 但是目前我们使用比较多的是 vim 编辑器. vim 具有程序编辑的能力,可以主 ...
- 渗透测试-奇技淫巧(一)--源IP地址隐藏
切记,切记.本文只作为技术交流,提醒各位注意网络安全,请勿用于其它用途,否则后果自付. 在很多时候,某某不希望不愿意有人溯源他的地址.他们是如何隐藏IP的? 今天来浅析下,如何隐藏源地址. 用到的工具 ...
- “不要抄代码!自己的代码也不要抄!”
在 Adventure 位于深圳的电子设备组装厂(SZE)里,小朱狠狠的对自己说. 他刚刚在调试 STM32F407VG 的 SPI 功能.就在昨天,他刚刚调试好了 STM32F407VG 的 USA ...
- iOS 二维码扫描
// 导入 AVFoundation.framwork 框架#import "HDCodeViewController.h" #import "HDNormalViewC ...
- IOS开发基础知识--碎片3
十二:判断设备 //设备名称 return [UIDevice currentDevice].name; //设备型号,只可得到是何设备,无法得到是第几代设备 return [UIDevice cur ...
- 【代码笔记】iOS-获取系统完成任务所需的后台时间
一,代码. AppDelegate.h #import <UIKit/UIKit.h> @interface AppDelegate : UIResponder <UIApplica ...
- python之选课系统详解[功能未完善]
作业需求 思路:1.先写出大体的类,比如学校类,学生类,课程类-- 2.写出类里面大概的方法,比如学校类里面有创建讲师.创建班级-- 3.根据下面写出大致的代码,并实现其功能 遇到的困 ...