Manacher算法——求最长回文子串
首先,得先了解什么是回文串。回文串就是正反读起来就是一样的,如“abcdcba”。我们要是直接采用暴力方法来查找最长回文子串,时间复杂度为O(n^3),好一点的方法是枚举每一个字符,比较较它左右距离相邻的点是否相等,我们姑且称之为中心检测法,时间复杂度为O(n^2)。
当我们遇到字符串为“aaaaaaaaa”,之前的算法就会发生各个回文相互重叠的情况,会产生重复计算,然后就产生了一个问题,能否改进?答案是能,1975年,一个叫Manacher发明了Manacher Algorithm算法,俗称马拉车算法,其时间复杂为O(n)。该算法是利用回文串的特性来避免重复计算的。
在中心检测法中,我们在遍历的过程要考虑到回文串长度的奇偶性,比如说“abba”的长度为偶数,“abcba”的长度为奇数,这样在寻找最长回文子串的过程要分别考奇偶的情况,是否可以统一处理了?
Manacher算法:
一)第一步是改造字符串S,变为T,其改造的方法如下:
在字符串S的字符之间和S的首尾都插入一个“#”,如:S=“abba”变为T="#a#b#b#a#" 。我们会发现S的长度是4,而T的长度为9,长度变为奇数了!!那S的长度为奇数的情况时,变化后的长度还是奇数吗?我们举个例子,S=“abcba”,变化为T=“#a#b#c#b#a#”,T的长度为11,所以我们发现其改造的目的是将字符串的长度变为奇数,这样就可以统一的处理奇偶的情况了。
二)第二步,为了改进回文相互重叠的情况,我们将改造完后的T[ i ] 处的回文半径存储到数组P[ ]中,P[ i ]为新字符串T的T[ i ]处的回文半径,表示以字符T[i]为中心的最长回文字串的最端右字符到T[i]的长度,如以T[ i ]为中心的最长回文子串的为T[ l, r ],那么P[ i ]=r-i+1。这样最后遍历数组P[ ],取其中最大值即可。若P[ i ]=1表示该回文串就是T[ i ]本身。举一个简单的例子感受一下:
数组P有一性质,P[ i ]-1就是该回文子串在原字符串S中的长度 ,那就是P[i]-1就是该回文子串在原字符串S中的长度,至于证明,首先在转换得到的字符串T中,所有的回文字串的长度都为奇数,那么对于以T[i]为中心的最长回文字串,其长度就为2*P[i]-1,经过观察可知,T中所有的回文子串,其中分隔符的数量一定比其他字符的数量多1,也就是有P[i]个分隔符,剩下P[i]-1个字符来自原字符串,所以该回文串在原字符串中的长度就为P[i]-1。
另外,由于第一个和最后一个字符都是#号,且也需要搜索回文,为了防止越界,我们还需要在首尾再加上非#号字符,实际操作时我们只需给开头加上个非#号字符,我就直接使用美元吧$,结尾不用加的原因是字符串的结尾标识为'\0',等于默认加过了。这样原问题就转化成如何求数组P[ ]的问题了。
三)如何求数组P [ ]
从左往右计算数组P[ ], Mi为之前取得最大回文串的中心位置,而R是最大回文串能到达的最右端的值。
1)当 i <=R时,如何计算 P[ i ]的值了?毫无疑问的是数组P中点 i 之前点对应的值都已经计算出来了。利用回文串的特性,我们找到点 i 关于 Mi 的对称点 j ,其值为 j= 2*Mi-i 。因,点 j 、i 在以Mi 为中心的最大回文串的范围内([L ,R]),
a)那么如果P[j] <R-i (同样是L和j 之间的距离),说明,以点 j 为中心的回文串没有超出范围[L ,R],由回文串的特性可知,从左右两端向Mi遍历,两端对应的字符都是相等的。所以P[ j ]=P[ i ](这里得先从点j转到点i 的情况),如下图:
b)如果P[ j ]>=R-i (即 j 为中心的回文串的最左端超过 L),如下图所示。即,以点 j为中心的最大回文串的范围已经超出了范围[L ,R] ,这种情况,等式P[ j ]=P[ i ]还成立吗?显然不总是成立的!因,以点 j 为中心的回文串的最左端超过L,那么在[ L, j ]之间的字符肯定能在( j, Mi ]找到相等的,由回文串的特性可知,P[ i ] 至少等于R- i,至于是否大于R-i(图中红色的部分),我们还要从R+1开始一一的匹配,直达失配为止,从而更新R和对应的Mi以及P[ i ]。
2)当 i > R时,如下图。这种情况,没法利用到回文串的特性,只能老老实实的一步步去匹配。
总体看来,Manacher可以看出是中心检测法的一种优化,我们会发现如果把这一行代码删除
if(i<mx)
{
p[i]=min(p[*id-i],mx-i);
}
else
{
p[i]=;
}
程序也是可以运行的,但这样是不是又变成了中心检测法了?我认为Manacher算法的最大优点在于利用回文串对称的性质,在处理p数组的时候,利用指针i,j的同步性,避免了匹配失败后的下标回退,因而将时间复杂度优化为了O(n)。
相应的代码如下:
char x[MAX];
char s_new[MAX*];
int p[MAX*];
int Init()
{
int i,j,len;
len=strlen(x);
s_new[]='$';
s_new[]='#';
j=;
for(i=; i<len; i++)
{
s_new[j++]=x[i];
s_new[j++]='#';
}
s_new[j]='\0';
return j;
}
int Manacher()
{
int i;
int len=Init();
int max_len=-;
int id;
int mx=;
for (i=; i<len; i++)
{
if(i<mx)
{
p[i]=min(p[*id-i],mx-i);
}
else
{
p[i]=;
}
while (s_new[i-p[i]]==s_new[i+p[i]])
{
p[i]++;
}
if(mx<i+p[i])
{
id=i;
mx=i+p[i];
}
}
for(i=; i<len; i++)
{
if(max_len<p[i]-)
{
max_len=p[i]-;
}
}
return max_len;
}
Manacher算法——求最长回文子串的更多相关文章
- manacher算法求最长回文子串
一:背景 给定一个字符串,求出其最长回文子串.例如: s="abcd",最长回文长度为 1: s="ababa",最长回文长度为 5: s="abcc ...
- Manacher算法 求 最长回文子串
1 概述(扯淡) 在了解Manacher算法之前,我们得先知道什么是回文串和子串. 回文串,就是正着看反着看都一样的字符串.比如说"abba"就是一个回文串,"abbc& ...
- Manacher算法 - 求最长回文串的利器
求最长回文串的利器 - Manacher算法 Manacher主要是用来求某个字符串的最长回文子串. 不要被manacher这个名字吓倒了,其实manacher算法很简单,也很容易理解,程序短,时间复 ...
- manacher算法求最长回文子序列
一:背景 给定一个字符串,求出其最长回文子串.例如: s="abcd",最长回文长度为 1: s="ababa",最长回文长度为 5: s="abcc ...
- 使用manacher算法解决最长回文子串问题
要解决的问题 求一个字符串最长回文子串是什么.且时间复杂度 O(N) 具体描述可参考: LeetCode_5_最长回文子串 LintCode_200_最长回文子串 暴力解法 以每个字符为中心向左右两边 ...
- hdu 3068 最长回文 (Manacher算法求最长回文串)
参考博客:Manacher算法--O(n)回文子串算法 - xuanflyer - 博客频道 - CSDN.NET 从队友那里听来的一个算法,O(N)求得每个中心延伸的回文长度.这个算法好像比较偏门, ...
- 小白月赛13 B小A的回文串 (马拉车算法求最长回文子串)
链接:https://ac.nowcoder.com/acm/contest/549/B来源:牛客网 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 262144K,其他语言52428 ...
- leetcode 5 Longest Palindromic Substring(Manacher算法求最长回文串)
应用一下manacher算法就可以O(n)求出结果了.可以参考hdu3068 substr(start,length)函数是这样用的: substr 方法 返回一个从指定位置开始,并具有指定长度的子字 ...
- Manacher算法求最长回文串模板
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> ...
随机推荐
- GoogleTest初探(2)
前面的随笔介绍了Google Test中的基本测试单元TEST和测试夹具TEST_F,此篇介绍区别于这两种测试的参数化测试TEST_P 当待测试方法的行为取决于传入的参数时,而且这些参数的不同组合有多 ...
- WebGl 缩放(矩阵变换)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Html5 Canvas 实现图片合成
多个图片合成一张 <!doctype html> <html> <head> <meta charset="utf-8"> < ...
- 使用抓包工具pproxy
下载地址:https://github.com/hidu/pproxy 一.下载后解压 二.打开pproxy.exe 三.手机连接wifi,设置wifi,代理改为手动,输入代理主机ip,代理主机端口. ...
- php实现银联支付
银联支付用的还是比较少的,而且开发中也没接触多少,不过因为工作项目用银联支付能降低费率,所以还是接入了银联支付.本文支付为银联网关和WAP支付接口. 官方网站SDK&DEMO:https:// ...
- STM32(5)——通用定时器基本定时器
1.STM32的Timer简介 STM32中一共有11个定时器,其中2个高级控制定时器,4个普通定时器和2个基本定时器,以及2个看门狗定时器和1个系统嘀嗒定时器. 其中系统嘀嗒定时器是前文中所描述的S ...
- BurpSuite系列(一)----Proxy模块(代理模块)
一.简介 Proxy代理模块作为BurpSuite的核心功能,拦截HTTP/S的代理服务器,作为一个在浏览器和目标应用程序之间的中间人,允许你拦截,查看,修改在两个方向上的原始数据流. Burp 代理 ...
- 实现虚拟机和宿主机之间的复制、粘贴(ubuntu系统)
参考:https://blog.csdn.net/weixin_37850264/article/details/79057886 https://blog.csdn.net/zldz14/artic ...
- Flex4中的拖动技术
下面列一个最简单的例子,在Flex中,拖动原来如此简单 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?xm ...
- 20155218 2006-2007-2 《Java程序设计》第一周学习总结
20155218 2006-2007-2 <Java程序设计>第1周学习总结 教材学习内容总结 浏览教材每章提出一个问题 组建如何与容器互动 PATH与classpath的对比 java的 ...