原文地址:https://segmentfault.com/a/1190000003914228   http://blog.csdn.net/synapse7/article/details/18908413

灰常不错的学习资料

先预处理下:在每个字符的两边都插入一个特殊的符号,比如abba变成#a#b#b#a#,aba变成 #a#b#a#(因为Manacher算法只能处理奇数长度的字符串)。同时,为了避免数组越界,在字符串开头添加另一特殊符号,比如$#a#b#a#。

以字符串3212343219为例,处理后变成S[] = "$#3#2#1#2#3#4#3#2#1#9#"。
然后用一个数组Len[i]来记录以处理后的字符S[i]为中心的最长回文子串的半长度(包括S[i]):

 S   #  #  #  #  #  #  #  #  #  #  #
Len

最终的回文子串的长度即为maxLen-1)

Manacher算法的核心就在于减少Len[i]的计算量,使得原来O(n^2)的算法优化为O(n)。

下面两幅图的红框中的字符串为当前的右边界下标最大的回文子串,mid为其中心,right为其最右端+1,i'=2*mid-i为i关于mid的对称点。

现要计算Len[i],若以i'为中心的回文串(黄框)包含在最长回文子串中,则由回文串的对称性,以i为中心的回文串亦在最长回文子串中,即有Len[i]=Len[2*mid-i]

若以i'为中心的回文串(黄框)不包含在最长回文子串中,则以i为中心的回文串的半长度Len[i]=right-i+(之后继续判断的长度)

那么,为什么复杂度是O(n)的呢?

首先,主要影响复杂度的是s[i + len[i]] == s[i - len[i]]这一判断。

由下面的代码可知,当i<right时,我们就用常数的时间计算Len[i](此时不会执行while中的语句);当i>=right时,我们就继续判断:while (s[i + len[i]] == s[i - len[i]]) ++len[i]; 结束后,right < i + len[i]为真,更新right值。这样,我们至多进行n次s[i + len[i]] == s[i - len[i]]判断,故复杂度为O(n)。

完整代码:

 #include<bits/stdc++.h>
using namespace std;
const int mx = ; char ss[mx + ], s[(mx << ) + ]; /// ss为源串,s为处理后的字符串
int len[(mx << ) + ]; void debug()
{
int i;
for (i = ; s[i]; ++i) printf("%c ", s[i]);
puts("");
for (i = ; s[i]; ++i) printf("%d ", len[i]);
puts("");
} int main()
{
int right, mid, i, maxlen;
while (gets(ss))
{
memset(s, , sizeof(s));
s[] = '$';
for (i = ; ss[i]; ++i) s[(i << ) + ] = '#', s[(i << ) + ] = ss[i];
s[(i << ) + ] = '#';
memset(len, , sizeof(len));
maxlen = right = mid = ;
for (i = ; s[i]; ++i)
{
len[i] = (i < right ? min(len[(mid << ) - i], right - i) : );
/* 取min的原因:记点i关于mid的对称点为i',
若以i'为中心的回文串范围超过了以mid为中心的回文串的范围
(此时有i + len[(mid << 1) - i] >= right,注意len是包括中心的半长度)
则len[i]应取right - i(总不能超过边界吧) */
while (s[i + len[i]] == s[i - len[i]]) ++len[i];
maxlen = max(maxlen, len[i]);
if (right < i + len[i]) mid = i, right = i + len[i];
}
printf("%d\n", maxlen - );
debug();
}
return ;
}

补充:使用 Manacher 算法后我们得到了一个 len 数组,利用它我们可以在 O(1) 的时间内判断该字符串的任意子串是不是回文串,方法如下:

 inline bool Query(int l, int r) /// 判断源串中的某一子串 ss[l...r] 是否为回文串
{
return len[l + r + ] >= r - l + ;
}

0. 问题定义

最长回文子串问题:给定一个字符串,求它的最长回文子串长度。

如果一个字符串正着读和反着读是一样的,那它就是回文串。下面是一些回文串的实例:

12321 a aba abba aaaa tattarrattat(牛津英语词典中最长的回文单词)

1. Brute-force 解法

对于最长回文子串问题,最简单粗暴的办法是:找到字符串的所有子串,遍历每一个子串以验证它们是否为回文串。一个子串由子串的起点和终点确定,因此对于一个长度为n的字符串,共有n^2个子串。这些子串的平均长度大约是n/2,因此这个解法的时间复杂度是O(n^3)。

2. 改进的方法

显然所有的回文串都是对称的。长度为奇数回文串以最中间字符的位置为对称轴左右对称,而长度为偶数的回文串的对称轴在中间两个字符之间的空隙。可否利用这种对称性来提高算法效率呢?答案是肯定的。我们知道整个字符串中的所有字符,以及字符间的空隙,都可能是某个回文子串的对称轴位置。可以遍历这些位置,在每个位置上同时向左和向右扩展,直到左右两边的字符不同,或者达到边界。对于一个长度为n的字符串,这样的位置一共有n+n-1=2n-1个,在每个位置上平均大约要进行n/4次字符比较,于是此算法的时间复杂度是O(n^2)。

3. Manacher 算法

对于一个比较长的字符串,O(n^2)的时间复杂度是难以接受的。Can we do better?

先来看看解法2存在的缺陷。

1) 由于回文串长度的奇偶性造成了不同性质的对称轴位置,解法2要对两种情况分别处理;
2) 很多子串被重复多次访问,造成较差的时间效率。

缺陷2)可以通过这个直观的小

manacherO(n)求最长回文子串 hihocoder1032的更多相关文章

  1. hdu 3068 最长回文(manachar求最长回文子串)

    题目连接:hdu 3068 最长回文 解题思路:通过manachar算法求最长回文子串,如果用遍历的话绝对超时. #include <stdio.h> #include <strin ...

  2. PAT甲题题解-1040. Longest Symmetric String (25)-求最长回文子串

    博主欢迎转载,但请给出本文链接,我尊重你,你尊重我,谢谢~http://www.cnblogs.com/chenxiwenruo/p/6789177.html特别不喜欢那些随便转载别人的原创文章又不给 ...

  3. Manacher模板( 线性求最长回文子串 )

    模板 #include<stdio.h> #include<string.h> #include<algorithm> #include<map> us ...

  4. 求最长回文子串 - leetcode 5. Longest Palindromic Substring

    写在前面:忍不住吐槽几句今天上海的天气,次奥,鞋子里都能养鱼了...裤子也全湿了,衣服也全湿了,关键是这天气还打空调,只能瑟瑟发抖祈祷不要感冒了.... 前后切了一百零几道leetcode的题(sol ...

  5. 后缀数组 - 求最长回文子串 + 模板题 --- ural 1297

    1297. Palindrome Time Limit: 1.0 secondMemory Limit: 16 MB The “U.S. Robots” HQ has just received a ...

  6. Manacher算法 O(n) 求最长回文子串

    转自:http://bbs.dlut.edu.cn/bbstcon.php?board=Competition&gid=23474 其实原文说得是比较清楚的,只是英文的,我这里写一份中文的吧. ...

  7. Manacher算法——求最长回文子串

    首先,得先了解什么是回文串.回文串就是正反读起来就是一样的,如“abcdcba”.我们要是直接采用暴力方法来查找最长回文子串,时间复杂度为O(n^3),好一点的方法是枚举每一个字符,比较较它左右距离相 ...

  8. manacher算法求最长回文子串

    一:背景 给定一个字符串,求出其最长回文子串.例如: s="abcd",最长回文长度为 1: s="ababa",最长回文长度为 5: s="abcc ...

  9. 求最长回文子串,O(n)复杂度

    最长回文子串问题-Manacher算法 最长回文串问题是一个经典的算法题. 0. 问题定义 最长回文子串问题:给定一个字符串,求它的最长回文子串长度. 假设一个字符串正着读和反着读是一样的,那它就是回 ...

随机推荐

  1. node14---分层结构数据库操作

    /**回调函数(函数作为参数): 0. 外层函数调用的地方,一定是外层函数体先执行,回调函数和普通函数地址一样,然后看函数体规定回调函数怎么执行. 1. 异步时候使用回调函数, 无论是否异步,回调函数 ...

  2. keras安装及使用

    安装全称参考https://keras-cn.readthedocs.io/en/latest/for_beginners/keras_linux/ 环境中已配置cuda8.0.cudnn5.0,ub ...

  3. bzoj1797: [Ahoi2009]Mincut 最小割(最小割+强联通tarjan)

    1797: [Ahoi2009]Mincut 最小割 题目:传送门 题解: 感觉是一道肥肠好的题目. 第二问其实比第一问简单? 用残余网络跑强联通,流量大于0才访问. 那么如果两个点所属的联通分量分别 ...

  4. bzoj3436: 小K的农场(差分约束)

    3436: 小K的农场 题目:传送门 题解: 查分基础: t==1  a>=b+c t==2  b>=a-c t==3  a>=b+0 b>=a+0 跑最长路一A 代码: #i ...

  5. Elasticsearch yellow 意味着主分片可用,副本不可用

    摘自:http://unasm.com/2016/11/644/ 在通过 /_cluster/state 命令查看es 状态的时候,发现es 处于一个yellow的状态, 这个很奇怪,按照官方的解释, ...

  6. 高斯滤波及高斯卷积核C++实现

    高斯滤波是一种线性平滑滤波,适用于消除高斯噪声,在图像处理的降噪.平滑中应用较多,特别是对抑制或消除服从正态分布的噪声非常有效. 高斯滤波的过程其实就是对整幅图像进行加权平均操作的过程.滤波后图像上每 ...

  7. keepalived+双主架构部署

    在高可用集群环境中,keepalived使用的是VIP,利用keepalived自带的服务监控功能和自定义脚本来实现MYSQL故障时自带切换. Keepalived基于VRRP协议,虚拟冗余路由协议, ...

  8. hadoop 2.6.0 分布式 + Spark 1.1.0 集群环境

    配置jdk 执行 sudo apt-get install openjdk-7-jdk jdk被安装到了 /usr/lib/jvm/ 目录 配置hosts 使用 vim 打开 /etc/hosts, ...

  9. Elasticsearch之中文分词器插件es-ik的自定义热更新词库

    不多说,直接上干货! 欢迎大家,关注微信扫码并加入我的4个微信公众号:   大数据躺过的坑      Java从入门到架构师      人工智能躺过的坑         Java全栈大联盟       ...

  10. Java多线程编程模式实战指南(三):Two-phase Termination模式--转载

    本文由本人首次发布在infoq中文站上:http://www.infoq.com/cn/articles/java-multithreaded-programming-mode-two-phase-t ...