layout: post

title: Manacher(马拉车)算法

date: 2019-09-07

author: xiepl1997

cover: 'assets/img/manacher.png'

tags: 敲敲敲

Manacher’s Alogrithm,中文名叫马拉车算法,是一位叫Manacher的人在1975年提出的一种算法,解决的问题是求最长回文子串,算法的神奇之处就在于将时间复杂度精进到了O(N)。还记得在两年前的四省赛中,有一道关于回文的题,题解就是用马拉车算法做解的,然而我们没有做出来。

01 由来

在求解最长回文子串时,一般的思路是以当前字符为中心向两边扩展寻找回文,但复杂度是O(N^2),那能不能将复杂度降低到线性?马拉车算法就是为此诞生的。

02 预处理

为了在处理字符串的时候不需要为字符串长度是奇数还是偶数而分别考虑,将对原始字符串进行处理,在每一个字符的左右两边都加上特殊字符(肯定不存在原始字符串中的字符),让字符串变成一个长度为奇数的字符串。如: abba --> #a#b#b#a#

03 计算最长回文子串长度

以字符串"arddrb"为例,将预处理后的新字符串"#a#r#d#d#r#b#",作为一个新的字符串arr,定义一个辅助数组Len,Len的长度与arr等长,用Len[i]来表示以arr[i]字符为中心的最长回文半径。

在等待Len数组计算出来之后,取Len数组中值最大的数所对应的下标,就是arr字符串中的所对应的字符为中心的回文串的半径,Len数组有一个特点:Len[i]-1的值,就是原字符串中该以该字符为中心的回文串的长度。以下为i、arr、Len对应的值

i     0 1 2 3 4 5 6 7 8 9 10 11 12
arr # a # r # d # d # r # b #
Len 1 2 1 2 1 2 5 2 1 2 1 2 1

04 计算回文子串起始索引

取出Len数组中最大的值的索引i后,应该如何由得到原字符串该字符的索引呢?继续以str="arddrb"为例,有arr="#a#r#d#d#r#b#",Len[6]=5,发现6-Len(6)=1,即i-Len[i]就是arr[i]字符在原始字符串中的下标。但以str="aba"为例,arr="#a#b#a#",Len[3]=4,3-Len[3]=-1,所以str[-1]将会溢出。为了避免奇回文溢出,所以在arr的首尾再分别添加一个特殊字符,如下

i     0 1 2 3 4 5 6 7 8 9 10
arr @ # c # a # b # a # $
Len 1 1 2 1 2 1 4 1 2 1 1

可以看出,对于b字符来说,6-Len(6)=2,可以得到最长回文子串的起始索引为(i-Len(i))/2。

05 计算Len数组

第三步和第四部都是以Len数组计算完成为前提来进行的,计算Len数组就是马拉车算法的主要工作了,还是以"arddrb"为例,

i     0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
arr @ # a # r # d # d # r # b # $
Len 1 1 2 1 2 1 2 5 2 1 2 1 2 1 1

定义两个变量Mi和R,Mi是所有回文子串中,能延伸到最右端位置的那个回文子串的中心点位置,R是该回文串能延伸到最右端的位置。

当i=7时,Len[i]=5,在以位置7位中心点的回文子串中,该回文串的右边界是位置12。

当i=12时,Len[i]=2,在以位置12位中心点的回文子串中,该回文串的右边界是位置14。

所以可以得到,R=Len[i]+i

具体编程时,从左往右计算数组Len,需要分以下情况

1) 当i <= R时,首先毫无疑问Len数组中i点之前的对应的值已经求出来了,利用回文的特点,只要找到i关于Mi点对称的点j,j=Mi* 2-i,i、j在以Mi为中心的回文串的范围内[L, R]:

  • 如果Len[j] < R-i(同样是L到j的距离),说明以j为中心的回文串没有超出范围[L, R],所以,Len[i] = Len(j),如下图

  • 如果Len[j] >= R-i,即j为中心的回文串的最左端超过L,如下图所示,所以Len[i] = Len[j]是不成立的,有回文串的特性可知,Len[i] 至少等于R-i,至于是否大于R-i,那还得需要从R+1开始一一匹配,直到失配为止,从而更新R和对应的中心点Mi以及Len[i]。

2) 当i > R时,如下图,这种情况没法用到回文串的特性,只能老实地去一一匹配。

代码如下(以leetcode第5题为例)

public String longestPalindrome(String s) {
int mi = 0;
int right = 0;
int maxlength = 0;
int maxpoint = 0;
String temp = "@#";
for(int i = 0; i < s.length(); i++){
temp += s.charAt(i);
temp += "#";
}
temp += "*";
int[] p = new int[temp.length()];
for(int i = 0; i < temp.length(); i++){
p[i] = 0;
}
for(int i = 1; i < temp.length()-1; i++){
p[i] = right > i? Math.min(p[2*mi-i], right - i) : 1;
while(temp.charAt(i+p[i]) == temp.charAt(i-p[i])){
p[i]++;
}
if(i + p[i] > right){
right = i + p[i];
mi = i;
}
if(maxlength < p[i]){
maxlength = p[i];
maxpoint = i;
}
}
//(maxpoint - maxlength)/2为最长回文数的起始点,maxlength为最长回文数的长度
return s.substring((maxpoint - maxlength)/2, (maxpoint - maxlength)/2 + maxlength - 1);
}

理解p[i] = right > i? Math.min(p[2* mi-i], right - i) : 1;就将manacher理解的差不多了。

Manacher(马拉车)算法(jekyll迁移)的更多相关文章

  1. manacher(马拉车算法)

    Manacher(马拉车算法) 序言 mannacher 是一种在 O(n)时间内求出最长回文串的算法 我们用暴力求解最长回文串长度的时间复杂度为O(n3) 很明显,这个时间复杂度我们接受不了,这时候 ...

  2. HDU - 3068 最长回文manacher马拉车算法

    # a # b # b # a # 当我们遇到回判断最长回文字符串问题的时候,若果用暴力的方法来做,就是在字符串中间添加 #,然后遍历每一个字符,找到最长的回文字符串.那么马拉车算法就是在这个基础上进 ...

  3. Manacher (马拉车) 算法:解决最长回文子串的利器

    最长回文子串 回文串就是原串和反转字符串相同的字符串.比如 aba,acca.前一个是奇数长度的回文串,后一个是偶数长度的回文串. 最长回文子串就是一个字符串的所有子串中,是回文串且长度最长的子串. ...

  4. manacher马拉车算法

    Manacher算法讲解 总有人喜欢搞事情,出字符串的题,直接卡掉了我的40分 I.适用范围 manacher算法解决的是字符串最长回文子串长度的问题. 关键词:最长 回文 子串 II.算法 1.纯暴 ...

  5. 最长回文子串 —— Manacher (马拉车) 算法

    最长回文子串 回文串就是原串和反转字符串相同的字符串.比如 aba,acca.前一个是奇数长度的回文串,后一个是偶数长度的回文串. 最长回文子串就是一个字符串的所有子串中,是回文串且长度最长的子串. ...

  6. Manacher(马拉车)算法

    Manacher算法是一个求字符串的最长回文子串一种非常高效的方法,其时间复杂度为O(n).下面分析以下其实行原理及代码: 1.首先对字符串进行预处理 因为回文分为奇回文和偶回文,分类处理比较麻烦,所 ...

  7. [模板] Manacher(马拉车)算法

    用途 求回文子串 做法 先考虑回文子串以某字符为中心的情况,即长度为奇数 推着做,记rad[i]为以i位置为中心的最大半径(包含中点) 考虑怎么求rad[i].找之前的一个右端点最靠右的位置p,设它的 ...

  8. Manacher(马拉车)算法详解

    给定一个字符串,求出其最长回文子串 eg:  abcba 第一步: 在字符串首尾,及各字符间各插入一个字符(前提这个字符未出现在串里). 如  原来ma  /*  a    b a    b   c ...

  9. Manacher's Algorithm 马拉车算法

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

随机推荐

  1. Burp Suite Scanner Module - 扫描模块

    Burp Suite Professional 和Enterprise Version的Scaner功能较丰富. 以Professional版本为例,包含Issue activity, Scan qu ...

  2. 题解 UVA501 【Black Box】

    思路与中位数一题,解决方案比较像,使用对顶堆来解决. 具体实现为,使用两个堆,大根堆维护较小的值,小根堆维护较大的值,即小根堆的堆顶是较大的数中最小的,大根堆的堆顶是较小的数中最大的. 将大于大根堆堆 ...

  3. PyCharm+PyQt5+Qt Designer配置

    配置前提 因为本机已经配置完毕了,本次使用的是虚拟机中的Win7系统,Win10系统操作步骤完全一样,无任何区别 PyCharm (这个不多说,官网下载安装,我是用的是2019.3版本) Python ...

  4. Git 撤销更改

    一.未使用 git add 缓存代码时 可以使用 git checkout -- filepathname (比如: git checkout -- readme.md  ,不要忘记中间的 “--” ...

  5. maven项目打包到本地库 两种方式

    方式一 1.项目根路径下 maven clean package 或者 maven package ,根据是否需要跳过代码中的测试代码 加上 -DskipTests 2.mvn install:ins ...

  6. BUUCTF-Web Easy Calc

    要素察觉 打开calc.php发现源码 过滤了很多字符.题目一开始提示了有waf,最后通过eval实现计算功能.考虑利用该函数读取flag文件,先尝试弹个phpinfo 被waf拦截,在num参数前面 ...

  7. PHPSTORM断点调试配置

    一.安装Xdebug xdebug官方提供了一个非常友好的安装指导: https://xdebug.org/wizard.php 打开上面的网站,将你的phpinfo页面输出的内容复制到表单中,然后点 ...

  8. 使用faker生成测试数据

    需要先安装faker模块,pip install faker 导入模块中的Faker类:from faker import Faker 实例化faker = Faker() print('姓名相关') ...

  9. 安装phpldapadmin

    关闭防火墙 [root@ldapmaster2 ~]# systemctl disable firewall [root@ldapmaster2 ~]# systemctl stop firewall ...

  10. Python之自定义函数

    函数 1.定义函数 在Python中定义一个函数要使用def语句,一次写出函数名.括号.括号中的的参数和冒号,然后在缩进块中编写函数体,函数的返回值用return返回.如下所示: def 函数名(参数 ...