使用manacher算法解决最长回文子串问题
要解决的问题
求一个字符串最长回文子串是什么。且时间复杂度
O(N)
具体描述可参考:
暴力解法
以每个字符为中心向左右两边扩,直到扩不动为止,记录下每个字符对应能扩的范围大小。因为有每个位置左右两边能扩的最大范围,我们可以很方便还原出最长回文子串是什么。
比如:AB1234321CD
这个字符串,以4
字符为中心向左右两边能扩的位置最大,1234321
为最长回文子串。
如上解法有个问题,即针对类似1ABBA2
这样的字符串,如上算法会错过最长回文子串ABBA
, 因为ABBA
不是以任何一个字符串向左右两边扩散得到的。所以,需要预处理一下原始字符串,预处理的方式如下:
在字符串的每两个位置之间插入一个特殊字符,变成一个预处理后的字符串,比如我们可以以#
作为特殊字符(特殊字符选哪个无所谓,不必非要是原始串中不含有的字符),将1ABBA2
这个字符串预处理成1#A#B#B#A#2
,用预处理串来跑这个暴力解法,会得到#A#B#B#A#
这个是预处理串的最长回文子串,我们可以很方便把这个串还原成原始串的最长回文子串。
暴力解法时间复杂度为O(N^2)
。
Manacher算法
Manacher算法可以用O(N)
时间复杂度解决这个问题。同样的,Manacher算法也需要对原始字符串进行上述的预处理过程。
相关变量说明
pArr
一个整型数组,长度和预处理串一样,存每个位置的最长回文半径是多少。
比如#A#B#B#A#
这个字符串,
位于数组2号位置的A
的回文半径是A#
或者#A
, 长度为2,则pArr[2] = 2
,
位于数组4号位置的#
的回文半径是#B#A#
或者#A#B#
, 长度为5, 则pArr[4] = 5
其他位置以此类推。
通过pArr
的定义,我们显然可以得到如下结论
pArr[0] = 1
i
整型,当前遍历到的位置,因为
pArr[0]=1
, 所以i可以从1开始遍历。
r
整型,回文最右边界,只要某个位置能扩到超过这个位置,就更新r这个值,初始值为0,因为一个字符串回文字符串至少是1,可以以第0个字符为中心且以0为最右边界(即:第0个字符本身作为一个回文串)
c
整型,就是扩到r位置的的中心点,即
pArr[c] = r - c + 1
,初始值为0,与r的初始值定为0一样的考虑。
流程
考虑i
, r
, c
三个变量之间的位置关系,无非有以下两种情况
情况1. i
在r
外,比如初始状态下:i=1, r,c = 0
情况2. i
在r
内或者i==r
关于情况1,流程如暴力解法一样,以i
位置为中心,左右两边扩到不能再扩的位置,更新pArr[i],c, r
的值。
关于情况2,我们假设i'
为i
关于c
对称的点,r'
为r
关于c
对称的点,示例图如下:
细分如下几种情况:
情况2-1
i'
自己的回文区域都在[r'...r]
内。
例如下图中[6...10]
为i'
的最长回文区域,左边界并未超过r'
由此可以推出,由于i
位置和i'
位置是关于c
位置对称的,则i
位置的回文区域至少包括[14...19]
这一段,如下图
即pArr[i']
至少等于pArr[i]
,接下来考虑i
能否继续扩散,即考虑19
位置的值是否等于13
位置的值,
我们可以假设19
位置的值和13
位置的值相等,同时,有如下两个显而易见的结论
19
位置的值等于5
位置的值。13
位置的值等于11
位置的值。
推出5
位置的值和11
位置的值相等,那么由于我们前面假设i'只能扩散到最左6
位置以及最右10
位置,所以,推出的结论和我们的假设矛盾,所以,19
位置的值不等于13
位置的值
所以情况2-1的结论是:i
的最长回文区域长度和i'
的答案一样, 即:pArr[i'] = pArr[i]
情况2-2
i'
自己的回文区域在[r'...r]
外
如下图
其中[2...14]
范围是以i'
为中心的最长回文区域。
在情况2-2下,我们可以得到如下几个结论:
根据
i
和i'
的关系,以i
为中心,从[13...19]
至少是回文的。根据
i'
的回文区域,12
位置的值等于4
位置的值,以c
为中心,4
位置的值又等于20
位置的值,所以12
位置的值等于20
位置的值,即以i
为中心,最长回文区域还可以扩展到[12...20]
。根据
i'
的回文区域,13
位置的值等于3
位置的值,以c
为中心,13
位置的值又等于11
位置的值,3
位置的值等于21
位置上的值,所以11
位置的值等于21
位置的值,即以i
为中心,最长回文区域还可以扩展到[11...21]
。继续判断以
i
为中心,是否可以继续扩散,即要继续判断10
位置的值是否等于22
位置的值,我们假设10
位置的值等于22
位置的值,以c
为中心,10
位置的值等于14
位置的值,以i'
为中心,14
位置的值等于2
位置的值,所以10
位置的值等于2
位置的值,根据我们的假设,2
位置的值会等于22
位置的值。这个与我们的前提矛盾了,因为我们的前提是c
只能扩展到[3...21]
这个区域,即:2
位置的值不可能等于22
位置的值,所以我们的假设不成立,所以10
位置的值不等于22
位置的值。
所以,情况2-2的结论是:i
到r
的距离就是i
的回文半径,即:pArr[i] = r - i + 1
情况2-3
i'
自己的回文区域左边界和r'
压线
如下图
其中[3...13]
区域为以i'
为中心能扩的最大回文区域。
有了情况2-2的铺垫,i
在情况2-3条件下至少可以扩充的范围是[11...21]
, 但是接下来是否可以继续扩充,还需要逐个判断。
自此,所有情况考虑完毕。
由于i在遍历过程中,始终不回退,所以,Manacher算法时间复杂度O(N)
完整代码
public class LeetCode_0005_LongestPalindromicSubstring {
public static String longestPalindrome(String s) {
if (s == null || s.length() <= 1) {
return s;
}
char[] str = s.toCharArray();
char[] strs = manacherStr(str);
int[] pArr = new int[strs.length];
int c = 0;
int r = 0;
int i = 1;
int len = strs.length;
int max = 1;
while (i < len) {
// pArr[i] 至少不需要扩的大小
pArr[i] = i < r ? Math.min(r - i, pArr[c - (i - c)]) : 1;
// 暴力扩
while (i + pArr[i] < len && i - pArr[i] >= 0) {
if (strs[i + pArr[i]] == strs[i - pArr[i]]) {
pArr[i]++;
} else {
break;
}
}
// 扩散的位置能否更新回文有边界R
// 如果可以更新,则更新R,且把C置于当前的i,因为是当前的i让回文右边界扩散的
if (i + pArr[i] > r) {
r = i + pArr[i];
c = i;
}
max = Math.max(pArr[i++], max);
}
// 定位最大回文有边界的回文中心是哪个
int n = 0;
for (; n < len; n++) {
if (pArr[n] == max) {
break;
}
}
// 构造最大回文子串
StringBuilder sb = new StringBuilder();
for (i = n - max + 2; i < n + max; i += 2) {
sb.append(strs[i]);
}
return sb.toString();
}
public static char[] manacherStr(char[] str) {
char[] strs = new char[str.length << 1 | 1];
for (int i = 0; i < strs.length; i++) {
strs[i] = ((i & 1) == 1) ? str[i >> 1] : '#';
}
return strs;
}
}
相关习题
LeetCode_0005_LongestPalindromicSubstring
LintCode_0200_LongestPalindromicSubstring
LeetCode_0647_PalindromicSubstrings
LeetCode_0214_ShortestPalindrome
更多
参考资料
使用manacher算法解决最长回文子串问题的更多相关文章
- Manacher算法——求最长回文子串
首先,得先了解什么是回文串.回文串就是正反读起来就是一样的,如“abcdcba”.我们要是直接采用暴力方法来查找最长回文子串,时间复杂度为O(n^3),好一点的方法是枚举每一个字符,比较较它左右距离相 ...
- manacher算法求最长回文子串
一:背景 给定一个字符串,求出其最长回文子串.例如: s="abcd",最长回文长度为 1: s="ababa",最长回文长度为 5: s="abcc ...
- Manacher算法 求 最长回文子串
1 概述(扯淡) 在了解Manacher算法之前,我们得先知道什么是回文串和子串. 回文串,就是正着看反着看都一样的字符串.比如说"abba"就是一个回文串,"abbc& ...
- Manacher (马拉车) 算法:解决最长回文子串的利器
最长回文子串 回文串就是原串和反转字符串相同的字符串.比如 aba,acca.前一个是奇数长度的回文串,后一个是偶数长度的回文串. 最长回文子串就是一个字符串的所有子串中,是回文串且长度最长的子串. ...
- Manacher's algorithm: 最长回文子串算法
Manacher 算法是时间.空间复杂度都为 O(n) 的解决 Longest palindromic substring(最长回文子串)的算法.回文串是中心对称的串,比如 'abcba'.'abcc ...
- Manacher算法 - 求最长回文串的利器
求最长回文串的利器 - Manacher算法 Manacher主要是用来求某个字符串的最长回文子串. 不要被manacher这个名字吓倒了,其实manacher算法很简单,也很容易理解,程序短,时间复 ...
- manacher算法求最长回文子序列
一:背景 给定一个字符串,求出其最长回文子串.例如: s="abcd",最长回文长度为 1: s="ababa",最长回文长度为 5: s="abcc ...
- 面试经典算法:马拉松算法,最长回文子串Golang实现
求一个字符串中最长的回文子串. package main import "fmt" /* 马拉松算法,求最长回文子串,时间复杂度:线性 */ func main() { // 回文 ...
- manacher 算法(最长回文串)
manacher算法: 定义数组p[i]表示以i为中心的(包含i这个字符)回文串半径长 将字符串s从前扫到后for(int i=0;i<strlen(s);++i)来计算p[i],则最大的p[i ...
随机推荐
- 解决win10快速访问不能取消固定
最近发现win10的快速访问不能取消固定,比如ftp和smb之类的都不能取消固定 最后百度了一下发现一个简易的方法: 在文件资源管理器地址栏输入:%APPDATA%\Microsoft\Windows ...
- SpringMVC学习05(整合ssm)
5.整合SSM 环境要求 环境: IDEA MySQL 5.7.19 Tomcat 9 Maven 3.6 要求: 需要熟练掌握MySQL数据库,Spring,JavaWeb及MyBatis知识,简单 ...
- 课程设计- 基于ssm的捐赠物资分配管理系统 && 基于java的申请救援管理系统
课程设计- 基于ssm的捐赠物资分配管理系统 && 基于java的申请救援管理系统 注意:该项目只展示部分功能,如需了解,评论区咨询即可. 1.开发环境 开发语言:Java 后台框架: ...
- Longhorn,企业级云原生容器分布式存储 - K8S 资源配置示例
内容来源于官方 Longhorn 1.1.2 英文技术手册. 系列 Longhorn 是什么? Longhorn 企业级云原生容器分布式存储解决方案设计架构和概念 Longhorn 企业级云原生容器分 ...
- noip18
T1 来自cf原题 考场直接暴力枚举 \(A,B\),15pts. 正解: 首先时间的表达式,\(T=\frac{A}{a_{i}}+\frac{B}{b_{i}}\),然后以\(\frac{1}{a ...
- 【协议】AAA Radius协议的常用报文分析
写在前面的话 RADIUS:Remote Authentication Dial In User Service,远程用户拨号认证系统由RFC2865,RFC2866定义,是应用最广泛的AAA协议. ...
- 数学log的基本知识
在数学中,对数是对求幂的逆运算,正如除法是乘法的倒数,反之亦然.这意味着一个数字的对数是必须产生另一个固定数字(基数)的指数, 在简单的情况下,乘数中的对数计数因子.如果a的x次方等于N(a>0 ...
- Spring Mvc原理分析(一)
Servlet生命周期了解 Servlet的生命(周期)是由容器(eg:Tomcat)管理的,换句话说,Servlet程序员不能用代码控制其生命. 加载和实例化:时机取决于web.xml的定义,如果有 ...
- Python3-sqlalchemy-orm 多对多关系建表、插入数据、查询数据
现在来设计一个能描述"图书"与"作者"的关系的表结构,需求是 一本书可以有好几个作者一起出版 一个作者可以写好几本书 此时你会发现,用之前学的外键好像没办法实现 ...
- 两种github action 打包.Net Core 项目docker镜像推送到阿里云镜像仓库
两种github action 打包.Net Core 项目docker镜像推送到阿里云镜像仓库 1.GitHub Actions 是什么? 大家知道,持续集成由很多操作组成,比如抓取代码.运行测试. ...