最长回文子串--轻松理解Manacher算法
最长回文子串这个问题的Manacher算法,看了很多博客,好不容易理解了,做一下记录。
这个算法的核心就是:将已经查询过的子字符串的最右端下标保存下来,在计算下标为i的回文字符串时,不需要从左右相邻的地方开始比较遍历,而是从某个初始值开始。
那么求这个初值就是该算法的关键。
1.字符串的初始化
先将字符串的每两个字符之间插入标识符,如“#”,然后在头尾也插入,插入什么符号这个其实影响不大。我是在头部和尾部也插入的“#”。这一步是为了让对称轴都在字符串数组中。
实例字符串 abada --> #a#b#a#d#a#
2.计算回文长度的初始
下标i从1 ~ length-1 开始遍历。同时借助2个辅助量 mid 和 maxRight ,maxRight 是用来存储我们扫描过的字符串的最右端的下标。mid为扫描最右端时的对称轴下标。注意,最右端的回文串不一定是最长的回文串。我们只是用maxRight来标识扫描长度。
我们用p[i]来存储回文字符串的单边长度。即以下标i的字符为对称轴的回文串的最右端与i的差值+1。回文串长度/2+1。
示例:
注意:maxRight对应下标时需要-1去对应。因为每次从center开始扩张时,结束条件是maxRight时center不能成为回文中心。
核心代码为这一句:
p[i] = maxRight > i ? Math.min(p[2*center-i],maxRight-i) : 1 ;
可能这样比较难以理解。我们可以结合以上的例子,来慢慢分析。
str[center] - str[maxRight-1]为我们的最右端回文字符串。我们维护这个字符串的位置。
当我们处理str[i]这个字符的回文长度时。
if ( maxRight > i){
//如果当前的位置已经被扫描过了(maxRight为我们扫描过的最右端)
//center肯定在左边,因为是随着i++扫描来更新center的
//假设j是i关于center的对称下标,即i+j=2*center
//string下标示意i图
//0----j---center---i----maxRight------length-1
int j = 2*center-i;
//算法核心:
//由于i j 关于center对称 而 center到maxRight在center~【center-maxRight】也是相同的
//另外由于j的最长回文串长度我们在计算i的时候已经算好了,那么可以认为i的周围也有这么p[j]长度的对称回文串
//center左右对称 j左右长度p[j]字符串对称 那么i左右也有对称字符串
//那么此时分为2种情况
//第一种 ---(j-p[j])--j--(j+p[i])-----center---(i-p[j])----i---(i+p[j])----maxRight
//第二种 ------(i-p[j])----center------i---maxRight----(i+p[j])
if ( p[j] + i<maxRight){
//第一种情况,已扫描范围内就已经找到最大值了
p[i] = p[j];
}else {
//第二张情况,maxRight太小,后面的可能相等,需要左右扩张来判断
p[i] = maxRight - i ;
}
}else {
//0-------maxRight--i
//p[i]还没有扫描呢,因此初始长度就是1
p[i] = 1;
}
上面的核心分析过程简化后就是上面的那一行代码。
核心代码理解了,后续的就简单了,从p[i]的初始值开始,向左右扩张,判断左右边界是不是相等,并同时更新center和maxRight的值。最后扫描p[]数组,其中最大的就是回文子串的长度,下标就是扩充了#的字符串下标。
最后,贴上源代码,大家看一看就可以理解了。
public static String longestPalindrome(String s) {
if ( s.length() <= 1 )return s ;
StringBuilder builder = new StringBuilder();
builder.append("#");
for (int i = 0; i <s.length() ; i++) {
builder.append(s.charAt(i));
builder.append("#");
}
int center = 0 ,maxRight = 0 ,len = builder.length();
int[] p = new int[len];
for (int i = 1; i < len-1; i++) {
//确定已扫描过的字符串中 最长回文串的初始值
//减少的时间复杂度 就在这里
//maxRight都被扫描,初值就不用从0开始了。
p[i] = maxRight > i ? Math.min(p[2*center-i],maxRight-i) : 1 ;
//确定p[i]的初始值后继续扩张判断回文
while (i+p[i] <len && i-p[i]>=0 &&builder.charAt(i+p[i])==builder.charAt(i-p[i])){
p[i]++;
}
//更新maxRight center
if ( i + p[i] > maxRight){
maxRight = i + p[i] ;
center = i ;
}
}
//遍历p数组求极大值
int mid= 0 , maxLength = 0 ;
for (int i = 1; i < builder.length(); i++) {
if ( p[i] > maxLength){
maxLength =p[i];
mid = i;
}
}
//分割字符串,消去"#"
return builder.toString().substring(mid-maxLength+1,mid+maxLength-1).replace("#","");
}
最长回文子串--轻松理解Manacher算法的更多相关文章
- 1089 最长回文子串 V2(Manacher算法)
1089 最长回文子串 V2(Manacher算法) 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 回文串是指aba.abba.cccbccc.aaaa ...
- 51Nod 1089:最长回文子串 V2(Manacher算法)
1089 最长回文子串 V2(Manacher算法) 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 回文串是指aba.abba.cccbccc.aaa ...
- 51NOD 1088 最长回文子串&1089 最长回文子串 V2(Manacher算法)
回文串是指aba.abba.cccbccc.aaaa这种左右对称的字符串. 输入一个字符串Str,输出Str里最长回文子串的长度. Input 输入Str(Str的长度 <= 1000(第二题要 ...
- [51Nod1089] 最长回文子串 V2(Manacher算法)
1089 最长回文子串 V2(Manacher算法) 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 回文串是指aba.abba.cccbccc.aaaa这种左右对称 ...
- hdu5371 最长回文子串变形(Manacher算法)
pid=5371">http://acm.hdu.edu.cn/showproblem.php? pid=5371 Problem Description Hotaru Ichijou ...
- 【回文字符串】 最长回文子串O(N) Manacher算法
原理讲的清晰:Manacher's ALGORITHM: O(n)时间求字符串的最长回文子串 注意: ①动态生命P[]和newStr数组后,不要忘记delete[] //其实这是基本的编码习惯 ②最终 ...
- 最长回文子串问题-Manacher算法
转:http://blog.csdn.net/dyx404514/article/details/42061017 Manacher算法 算法总结第三弹 manacher算法,前面讲了两个字符串相算法 ...
- 51nod 1089 最长回文子串 V2(Manacher算法)
回文串是指aba.abba.cccbccc.aaaa这种左右对称的字符串. 输入一个字符串Str,输出Str里最长回文子串的长度. 收起 输入 输入Str(Str的长度 <= 100000) ...
- 【51NOD-0】1089 最长回文子串 V2(Manacher算法)
[算法]回文树 #include<cstdio> #include<algorithm> #include<cstring> using namespace std ...
随机推荐
- struts2框架xml验证
struts2验证分为3步: 1.获取需要验证的信息,使用同名属性,提供getter,setter方法.然后框架使用反射将值自动注入. 2.对信息进行验证,成功失败作出对应的选择. xml验证和手动验 ...
- Spring 配置 详细
一.连接池概述 数据库连接池概述: 数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出.对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指 ...
- 百度地图API的第一次接触——标注和信息窗的使用
1.定义js函数,用于在指定位置添加标注,在标注位置添加并打开信息窗口 function addMarker(point, index){ // 创建图标对象 var myIcon = new BMa ...
- 设置一个.exe文件开机启动
运行"regedit",编辑注册表 HKEY_LOCAL_MACHINE -- SOFTWARE -- Microsoft -- Windows -- CurrentVersion ...
- Lagom学习 五 Hello world工程
用Maven创建一个Hello world的Lagom工程: 1: 在想创建工程的目下下,打开CMD 2: mvn archetype:generate -Dfilter=com.lightbend ...
- Sleep 比对 (Win32API 与 STL )
OutputDebugStringA("begin 1========"); for (int i = 0; i < 1800; i++) { Sleep(2); } Out ...
- Docker入门(七):部署app
这个<Docker入门系列>文档,是根据Docker官网(https://docs.docker.com)的帮助文档大致翻译而成.主要是作为个人学习记录.有错误的地方,Robin欢迎大家指 ...
- IOS要用到的零碎东西
有些东西虽然不重要,但是零零碎碎会用到,就做个笔录吧: 协议中有2个关键字可以控制方法是否要实现(默认是@required),在大多数情况下, 用途在于程序员之间的交流 @required:这个方法必 ...
- SpringBoot @RequestBody 中文乱码
今天突然想学习一下Restful风,详细的我就不赘述了,我的理解是同一个请求路径根据请求方式不同进行不同的处理 如四种提交方式,这里推荐一个插件Postman,可以模仿各种请求类型,自行百度安装吧 G ...
- java之二叉树--未完待续
参考http://how2j.cn/k/collection/collection-tree/476.html#nowhere 二叉树概念 二叉树由各种节点组成二叉树特点:每个节点都可以有左子节点,右 ...