Manacher's Algorithm针对的是最长回文子串问题。对于此问题,最直接的方法是遍历每一个元素,遍历过程中以每一个字符为中心向两边扩展以寻找此字符为中心的最长回文子串。复杂度O(n2)。Manacher算法将时间复杂度降至O(n),关键点在于将奇偶字串统一成奇数字串。

  方法是在每一个字符的左右都加上一个特殊字符,如'#'

  ”abccba" -> "#a#b#c#c#b#a#"

  "abcba"  -> "#a#b#c#b#a#"

  对于长度为n的字符串,需添加 n+1 个特殊字符,则新字符串长度为 2n+1。从而解决了奇偶字串分情况判断问题。

  假设原字符串为 str,添加特殊字符串后的新字符串为 trans,设立长度与 trans 相同的一个数组 p,p[i] 代表以 trans[i] 为中心的最长回文字串的半径,例如对于trans = "#a#b#c#", p[2] = 2(”#a#“), p[3] = 1(”#“)。  

  trans :# 1 # 2 # 2 # 1 # 2 # 2 #
  p       :1 2 1 2 5 2 1 6 1 2 3 2 1

  p 数组的一个性质是 p[i] - 1 即是以 trans[i](trans[i] 为原字符串中元素) 为中心的最长回文字串在原字符串S中的长度。证明:首先在转换得到的字符串 trans 中,所有的回文字串的长度都为奇数,那么对于以 trans[i] 为中心的最长回文字串,其长度就为 2*p[i]-1 ,经过观察可知,trans 中所有的回文子串,其中分隔符的数量一定比其他字符的数量多1,也就是有 p[i] 个分隔符,剩下 p[i]-1 个字符来自原字符串,所以该回文串在原字符串中的长度就为 p[i]-1。

  因为添加的特殊字符也需要搜索其最长回文字串,那么为了避免复杂的边界讨论,需要在字符串首尾各添加与之前添加的特殊字符不同的另一特殊字符作为边界。(通常只在首部添加,尾部不需要添加的原因是字符串的结尾为 '\0' ,相当于已经加过了。所以如果在某种情况下字符串没有默认的 '\0'结尾,那么需要人为的添加一个特殊字符)

  添加两个辅助变量,mx 和 id。mx是目前最长回文字串能延伸的最右位置。id 为 最长回文字串的对称轴所在位置。

  1. 当 mx > i

   最长回文字串为 (trans[2 * id - mx], trans[mx]).  i 在此最大回文字串中,j 为 i 关于 id 的对称点

    1) mx - i > p[j]

                    

    

        当 mx - i > p[j],说明 p[i] = p[j] = p[2*id - i]。因为 j 是 i 关于 id 的对称点,既然 以 j 为中心的最长回文字串都在 以 id 为中心的回文字串中,那么 以 i 为中心的回文字串也应该在以 id 为中心的回文字串中。假设 以 j 为中心的最长回文字串左端点为 begin,右端点为 end,那么由于 mx - i > p[j],所以 j - mx > p[j], 所以 begin > mx的对称点,end < id。相同的,由于以 i 为中心的回文字串也应该在 (id,mx)范围内,因为 i 和 j 都在回文串(mx对称点, mx)内,那么(mx, j)与(i,mx)相同。所以 p[i] = p[j]

    2) mx - i <= p[j]

      

        当 mx - i <= p[j] 时,以 j 为中心的回文子串不一定完全包含于以 id 为中心的回文子串中,但是基于对称性可知,图中两个绿框所包围的部分是相同的,也就是说以i 为中心的回文子串,其向右至少会扩展到 mx 的位置,也就是说 P[i] >= mx - i。至于 mx 之后的部分是否对称,只能通过遍历得知了。

  2. 当 mx <= i

    说明以 i 为对称轴的回文串还没有任何一个部分被访问过,于是只能从 i 的左右两边开始尝试扩展了,当左右两边字符不同,或者到达字符串边界时停止。然后更新 mx 和 id 。

  最长回文子串对应原串中的位置:l = (i - p[i])/2; r = (i + p[i])/2 - 2;

  

 #include <iostream>
#include <string.h>
#include <stdio.h> using namespace std;
const int N=<<; char T[N]; //原字符串
char S[N]; //转换后的字符串
int R[N]; //回文半径 void Init(char *T)
{
S[] = '$';
int len = strlen(T);
for(int i = ; i <= len; i++)
{
S[*i + ] = '#';
S[*i + ] = T[i];
}
} void Manacher(char *S)
{
int k = ,mx = ;
int len = strlen(S);
for(int i = 1; i < len; i++)
{
if(mx > i)
R[i] = R[*k - i] < mx - i? R[*k-i] : mx-i;
else
R[i] = ;
while(S[i + R[i]] == S[i - R[i]])
R[i]++;
if(R[i] + i > mx)
{
mx = R[i] + i;
k = i;
}
}
} int main()
{
while(~scanf("%s", T))
{
Init(T);
Manacher(S);
int len = strlen(S);
int ans = ;
for(int i = ; i<len; i++)
ans = R[i] > ans? R[i] : ans;
printf("%d\n", ans - );
}
return ;
}

转自:链接 ,链接

 注意:题目中的S[i + R[i]] == S[i - R[i]]实际上已经属于越界访问,但由于语言和编译器的不同,对于越界问题处理也不同。

【算法总结】Manacher's Algorithm的更多相关文章

  1. 什么是马拉车算法(Manacher's Algorithm)?

    提出问题 最长回文子串问题:给定一个字符串,求它的最长回文子串长度. 如果一个字符串正着读和反着读是一样的,那它就是回文串.如a.aa.aba.abba等. 暴力解法 简单粗暴:找到字符串的所有子串, ...

  2. 马拉车算法(Manacher's Algorithm)

    这是悦乐书的第343次更新,第367篇原创 Manacher's Algorithm,中文名叫马拉车算法,是一位名叫Manacher的人在1975年提出的一种算法,解决的问题是求最长回文子串,神奇之处 ...

  3. Manacher's Algorithm 马拉车算法

    这个马拉车算法Manacher‘s Algorithm是用来查找一个字符串的最长回文子串的线性方法,由一个叫Manacher的人在1975年发明的,这个方法的最大贡献是在于将时间复杂度提升到了线性,这 ...

  4. Manacher's Algorithm(马拉车算法)

    ## 背景 该算法用于求字符串的最长回文子串长度. ## 参考文章 >[最长回文子串——Manacher 算法](https://segmentfault.com/a/1190000003914 ...

  5. Manacher's Algorithm 马拉车算法(最长回文串)

    这个马拉车算法Manacher‘s Algorithm是用来查找一个字符串的最长回文子串的线性方法,由一个叫Manacher的人在1975年发明的,这个方法的最大贡献是在于将时间复杂度提升到了线性,这 ...

  6. Manacher's algorithm

    Manacher's algorithm 以\(O(n)\)的线性时间求一个字符串的最大回文子串. 1. 预处理 一个最棘手的问题是需要考虑最长回文子串的长度为奇数和偶数的情况.我们通过在任意两个字符 ...

  7. Manacher’s Algorithm (神啊)

    (转载自)http://blog.csdn.net/hopeztm/article/details/7932245 这里描述了一个叫Manacher’s Algorithm的算法. 算法首先将输入字符 ...

  8. Hash 算法与 Manacher 算法

    目录 前言 简单介绍 简述 Hash 冲突 离散化 基本结构 普通 Hash 简述 例题 字符串 Hash 简单介绍 核心思想 基本运算 二维字符串 Hash 例题 兔子与兔子 回文子串的最大长度 后 ...

  9. SHA1 安全哈希算法(Secure Hash Algorithm)

    安全哈希算法(Secure Hash Algorithm)主要适用于数字签名标准 (Digital Signature Standard DSS)里面定义的数字签名算法(Digital Signatu ...

随机推荐

  1. Powerdesigner Name与Comment的互相转换

    使用说明: 在[Tools]-[Execute Commands]-[Edit/Run Script] 下.输入下面你要选择的语句即可: 1.Name填充Comment '把pd中那么name想自动添 ...

  2. why factory pattern and when to use factory pattern

    1 factory pattern本质上就是对对象创建进行抽象 抽象的好处是显然的,可以方便用户去获取对象. 2 使用factory pattern的时机 第一,当一个对象的创建依赖于其它很多对象的时 ...

  3. Numerical Differentiation 数值微分

    zh.wikipedia.org/wiki/數值微分 数值微分是数值方法中的名词,是用函数的值及其他已知资讯来估计一函数导数的算法. http://mathworld.wolfram.com/Nume ...

  4. Java语言实现简单FTP软件------>连接管理模块的实现:主机与服务器之间的连接与关闭操作(八)

    (1)FTP连接 运行FTP客户端后,首先是连接FTP服务器,需要输入FTP服务器的IP地址及用户名.密码以及端口号后点击连接按钮开始连接FTP服务器,连接流程图如下图所示. 点击"连接&q ...

  5. Linux开启防火墙后,设置允许通过的端口

    安装Firewall命令: yum install firewalld firewalld-config Firewall开启端口命令: firewall-cmd --zone=public --ad ...

  6. Spring AOP 学习例子

    http://outofmemory.cn/code-snippet/3762/Spring-AOP-learn-example     工作忙,时间紧,不过事情再多,学习是必须的.记得以前的部门老大 ...

  7. python实例3-天气小模块

    调用中国天气的一小段代码,抓取 #! /usr/bin/python # coding = utf-8 # ToDo: get weather info from weather.com.cn # A ...

  8. 请简单介绍一下Spring

    Spring 是一个开源框架,是为了解决企业应用程序开发复杂性而创建的.框架的主要优势之一就是其分层架构,分层架构允许您选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架. Spring ...

  9. Microsoft Office Document Imaging批量ocr 方法

    先将pdf文件->导出->tiff文件,生成pdf每页的tiff文件 使用 G:\SoftWare-new\tiff文件合并拆分工具 将一个导出的单个tiff合并为一个tiff文件 再用 ...

  10. hadoop 根据secondary namenode恢复namenode

    refer to http://www.cnblogs.com/Richardzhu/p/3435989.html http://blog.csdn.net/wuzhilon88/article/de ...