Manacher(马拉车)算法(jekyll迁移)
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迁移)的更多相关文章
- manacher(马拉车算法)
Manacher(马拉车算法) 序言 mannacher 是一种在 O(n)时间内求出最长回文串的算法 我们用暴力求解最长回文串长度的时间复杂度为O(n3) 很明显,这个时间复杂度我们接受不了,这时候 ...
- HDU - 3068 最长回文manacher马拉车算法
# a # b # b # a # 当我们遇到回判断最长回文字符串问题的时候,若果用暴力的方法来做,就是在字符串中间添加 #,然后遍历每一个字符,找到最长的回文字符串.那么马拉车算法就是在这个基础上进 ...
- Manacher (马拉车) 算法:解决最长回文子串的利器
最长回文子串 回文串就是原串和反转字符串相同的字符串.比如 aba,acca.前一个是奇数长度的回文串,后一个是偶数长度的回文串. 最长回文子串就是一个字符串的所有子串中,是回文串且长度最长的子串. ...
- manacher马拉车算法
Manacher算法讲解 总有人喜欢搞事情,出字符串的题,直接卡掉了我的40分 I.适用范围 manacher算法解决的是字符串最长回文子串长度的问题. 关键词:最长 回文 子串 II.算法 1.纯暴 ...
- 最长回文子串 —— Manacher (马拉车) 算法
最长回文子串 回文串就是原串和反转字符串相同的字符串.比如 aba,acca.前一个是奇数长度的回文串,后一个是偶数长度的回文串. 最长回文子串就是一个字符串的所有子串中,是回文串且长度最长的子串. ...
- Manacher(马拉车)算法
Manacher算法是一个求字符串的最长回文子串一种非常高效的方法,其时间复杂度为O(n).下面分析以下其实行原理及代码: 1.首先对字符串进行预处理 因为回文分为奇回文和偶回文,分类处理比较麻烦,所 ...
- [模板] Manacher(马拉车)算法
用途 求回文子串 做法 先考虑回文子串以某字符为中心的情况,即长度为奇数 推着做,记rad[i]为以i位置为中心的最大半径(包含中点) 考虑怎么求rad[i].找之前的一个右端点最靠右的位置p,设它的 ...
- Manacher(马拉车)算法详解
给定一个字符串,求出其最长回文子串 eg: abcba 第一步: 在字符串首尾,及各字符间各插入一个字符(前提这个字符未出现在串里). 如 原来ma /* a b a b c ...
- Manacher's Algorithm 马拉车算法
这个马拉车算法Manacher‘s Algorithm是用来查找一个字符串的最长回文子串的线性方法,由一个叫Manacher的人在1975年发明的,这个方法的最大贡献是在于将时间复杂度提升到了线性,这 ...
随机推荐
- T2 监考老师 题解
第二题,他并不是多难的算法.甚至连搜索都不用,他的题目要求和数据断定了他第二题的地位. 在一个大试场里,有 n 行 m 列的考生,小王和众多同学正在考试,这时,有一部分考生 作弊,当然,监考老师能发现 ...
- oracle 启动报ORA-01105 ORA-19808
bash-4.4$ srvctl start instance -i jfcddb2 -d jfcddb PRCR-1013 : Failed to start resource ora.jfcddb ...
- STL源码剖析:配置器
作用:对内存的管理 接口:申请和释放 内容: 几个全局函数 一级配置器 二级配置器 准备知识 POD是什么: Plain Old Data简称POD,表示传统的C语言类型:与POD类型对应的是非POD ...
- Git 撤销更改
一.未使用 git add 缓存代码时 可以使用 git checkout -- filepathname (比如: git checkout -- readme.md ,不要忘记中间的 “--” ...
- Mybatis(三)动态sql语句
动态sql语句操作 1.MyBatis中#{ }和${ }的区别 在 mapper 中定义的参数传到 xml 中之后,在查询之前 mybatis 会对其进行动态解析.mybatis 为我们提供了两种支 ...
- 基于.Net Core的Redis实现查询附近的地理信息
1.使用的Redis客户端为:ServiceStack.Redis 2.Redis 中的 GEORedis是我们最为熟悉的K-V数据库,它常被拿来作为高性能的缓存数据库来使用,大部分项目都会用到它.从 ...
- SpringCloud系列使用Eureka进行服务治理
1. 什么是微服务? "微服务"一词来自国外的一篇博文,网站:https://martinfowler.com/articles/microservices.html 如果您不能看 ...
- 《Python编程初学者指南》高清PDF版|百度网盘免费下载|Python基础
<Python编程初学者指南>|百度网盘免费下载| 提取码:03b1 内容简介 Python是一种解释型.面向对象.动态数据类型的高级程序设计语言.Python可以用于很多的领域,从科学计 ...
- STL set
写在前面:STL大法好! 容器set,可以实现排序,插入元素不能重复(所以可能插入失败) 接下来我们看一下set的基本用法 begin() 返回set容器的第一个元素的地址 end() 返回s ...
- win10在html上运行java的applet程序
Applet是采用Java编程语言编写的小应用程序,该程序可以包含在 HTML(标准通用标记语言的一个应用)页中,与在页中包含图像的方式大致相同. 含有Applet的网页的HTML文件代码中部带有 和 ...