0. 问题定义

最长回文子串问题:给定一个字符串,求它的最长回文子串长度。

如果一个字符串正着读和反着读是一样的,那它就是回文串。下面是一些回文串的实例:

12321 a aba abba aaaa tattarrattat(牛津英语词典中最长的回文单词)

1. Brute-force 解法

对于最长回文子串问题,最简单粗暴的办法是:找到字符串的所有子串,遍历每一个子串以验证它们是否为回文串。一个子串由子串的起点和终点确定,因此对于一个长度为n的字符串,共有n^2个子串。这些子串的平均长度大约是n/2,因此这个解法的时间复杂度是O(n^3)。

2. 改进的方法

显然所有的回文串都是对称的。长度为奇数回文串以最中间字符的位置为对称轴左右对称,而长度为偶数的回文串的对称轴在中间两个字符之间的空隙。可否利用这种对称性来提高算法效率呢?答案是肯定的。我们知道整个字符串中的所有字符,以及字符间的空隙,都可能是某个回文子串的对称轴位置。可以遍历这些位置,在每个位置上同时向左和向右扩展,直到左右两边的字符不同,或者达到边界。对于一个长度为n的字符串,这样的位置一共有n+n-1=2n-1个,在每个位置上平均大约要进行n/4次字符比较,于是此算法的时间复杂度是O(n^2)。

3. Manacher 算法

对于一个比较长的字符串,O(n^2)的时间复杂度是难以接受的。Can we do better?

先来看看解法2存在的缺陷。

1) 由于回文串长度的奇偶性造成了不同性质的对称轴位置,解法2要对两种情况分别处理;
2) 很多子串被重复多次访问,造成较差的时间效率。

缺陷2)可以通过这个直观的小体现:

char: a b a b a
i : 0 1 2 3 4

当i==1,和i==2时,左边的子串aba分别被遍历了一次。

如果我们能改善解法2的不足,就很有希望能提高算法的效率。Manacher正是针对这些问题改进算法。

(1) 解决长度奇偶性带来的对称轴位置问题

Manacher算法首先对字符串做一个预处理,在所有的空隙位置(包括首尾)插入同样的符号,要求这个符号是不会在原串中出现的。这样会使得所有的串都是奇数长度的。以插入#号为例:

aba  ———>  #a#b#a#
abba ———> #a#b#b#a#

插入的是同样的符号,且符号不存在于原串,因此子串的回文性不受影响,原来是回文的串,插完之后还是回文的,原来不是回文的,依然不会是回文。

(2) 解决重复访问的问题

我们把一个回文串中最左或最右位置的字符与其对称轴的距离称为回文半径。Manacher定义了一个回文半径数组RL,用RL[i]表示以第i个字符为对称轴的回文串的回文半径。我们一般对字符串从左往右处理,因此这里定义RL[i]为第i个字符为对称轴的回文串的最右一个字符与字符i的距离。对于上面插入分隔符之后的两个串,可以得到RL数组:

char:    # a # b # a #
RL : 1 2 1 4 1 2 1
RL-1: 0 1 0 3 0 1 0
i : 0 1 2 3 4 5 6 char: # a # b # b # a #
RL : 1 2 1 2 5 2 1 2 1
RL-1: 0 1 0 1 4 1 0 1 0
i : 0 1 2 3 4 5 6 7 8

上面我们还求了一下RL[i]-1。通过观察可以发现,RL[i]-1的值,正是在原本那个没有插入过分隔符的串中,以位置i为对称轴的最长回文串的长度。那么只要我们求出了RL数组,就能得到最长回文子串的长度。

于是问题变成了,怎样高效地求的RL数组。基本思路是利用回文串的对称性,扩展回文串

我们再引入一个辅助变量MaxRight,表示当前访问到的所有回文子串,所能触及的最右一个字符的位置。另外还要记录下MaxRight对应的回文串的对称轴所在的位置,记为pos,它们的位置关系如下。

我们从左往右地访问字符串来求RL,假设当前访问到的位置为i,即要求RL[i],在对应上图,i必然是在po右边的(obviously)。但我们更关注的是,i是在MaxRight的左边还是右边。我们分情况来讨论。

1)当iMaxRight的左边

情况1)可以用下图来刻画:

我们知道,图中两个红色块之间(包括红色块)的串是回文的;并且以i为对称轴的回文串,是与红色块间的回文串有所重叠的。我们找到i关于pos的对称位置j,这个j对应的RL[j]我们是已经算过的。根据回文串的对称性,以i为对称轴的回文串和以j为对称轴的回文串,有一部分是相同的。这里又有两种细分的情况。

  1. j为对称轴的回文串比较短,短到像下图这样。

这时我们知道RL[i]至少不会小于RL[j],并且已经知道了部分的以i为中心的回文串,于是可以令RL[i]=RL[j]。但是以i为对称轴的回文串可能实际上更长,因此我们试着以i为对称轴,继续往左右两边扩展,直到左右两边字符不同,或者到达边界。

  1. j为对称轴的回文串很长,这么长:

这时,我们只能确定,两条蓝线之间的部分(即不超过MaxRight的部分)是回文的,于是从这个长度开始,尝试以i为中心向左右两边扩展,,直到左右两边字符不同,或者到达边界。

不论以上哪种情况,之后都要尝试更新MaxRightpos,因为有可能得到更大的MaxRight。

具体操作如下:

step 1: 令RL[i]=min(RL[2*pos-i], MaxRight-i)
step 2: 以i为中心扩展回文串,直到左右两边字符不同,或者到达边界。
step 3: 更新MaxRight和pos

2)当iMaxRight的右边

遇到这种情况,说明以i为对称轴的回文串还没有任何一个部分被访问过,于是只能从i的左右两边开始尝试扩展了,当左右两边字符不同,或者到达字符串边界时停止。然后更新MaxRightpos

实现的时候要注意不要漏掉对称位置延长的长度不超过当前最右的边界时,我们仍然应该尝试去往左右两边扩展

注意我的初始化细节...

当我们能找到i的对称位置时,那么i的左半部分一定能找全,画图等价代换即可

那么我们只关心i延长 RL[对称位置]的距离之后,超不超边界,超了边界,从零开始扩展

不超边界,那么我们就有了左右扩展RL[对称位置]的回文串,轻易能构造出以i为中心更长的回文串

所以我们仍然选择尝试左右扩展,但是我们这次直接从RL[对称位置]的长度开始扩展,节省了很多计算量

实现代码

#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std; const int maxn=1e5+7;
char s[maxn],t[maxn*3];
int RL[maxn*3],cnt;
void manacher(){
int pos,mxright,i,ans=0;
for(i=0,pos=-1,mxright=-1;i<cnt;++i){
if(i>mxright){
pos=i;
while(i-RL[i]>=0&&i+RL[i]<cnt&&t[i-RL[i]]==t[i+RL[i]]) RL[i]++;
mxright=i+RL[i]-1;
ans=max(ans,RL[i]-1);
}
else{
int op=2*pos-i;
if(i+RL[op]-1>mxright){
while(i-RL[i]>=0&&i+RL[i]<cnt&&t[i-RL[i]]==t[i+RL[i]]) RL[i]++;
ans=max(ans,RL[i]-1);
if(i+RL[i]-1>mxright){
pos=i;mxright=i+RL[i];
}
}
else{
RL[i]=RL[op];
while(i-RL[i]>=0&&i+RL[i]<cnt&&t[i-RL[i]]==t[i+RL[i]]) RL[i]++;
ans=max(ans,RL[i]-1);
if(i+RL[i]-1>mxright){
pos=i;mxright=i+RL[i];
}
}
}
}
printf("%d\n",ans);
}
int main(){
scanf("%s",s);
int len=strlen(s),j=0;
while(j<len){
if((cnt+1)&1)t[cnt++]='#';
else t[cnt++]=s[j++];
}
if(t[cnt-1]!='#') t[cnt++]='#';
manacher();
return 0;
}

51nod1089 最长回文子串 manacher算法的更多相关文章

  1. 九度OJ 1528 最长回文子串 -- Manacher算法

    题目地址:http://ac.jobdu.com/problem.php?pid=1528 题目描述: 回文串就是一个正读和反读都一样的字符串,比如"level"或者"n ...

  2. lintcode最长回文子串(Manacher算法)

    题目来自lintcode, 链接:http://www.lintcode.com/zh-cn/problem/longest-palindromic-substring/ 最长回文子串 给出一个字符串 ...

  3. 最长回文子串—Manacher 算法 及 python实现

    最长回文子串问题:给定一个字符串,求它的最长回文子串长度.如果一个字符串正着读和反着读是一样的,那它就是回文串.   给定一个字符串,求它最长的回文子串长度,例如输入字符串'35534321',它的最 ...

  4. hihocoder #1032 : 最长回文子串 Manacher算法

    题目链接: https://hihocoder.com/problemset/problem/1032?sid=868170 最长回文子串 时间限制:1000ms内存限制:64MB 问题描述 小Hi和 ...

  5. 5. Longest Palindromic Substring(最长回文子串 manacher 算法/ DP动态规划)

    Given a string s, find the longest palindromic substring in s. You may assume that the maximum lengt ...

  6. HiHo 1032 最长回文子串 (Manacher算法求解)

    /** * 求解最长回文字串,Manacher算法o(n)求解最长回文子串问题 **/ #include<cstdio> #include<cstdlib> #include& ...

  7. hihoCoder #1032 : 最长回文子串 [ Manacher算法--O(n)回文子串算法 ]

    传送门 #1032 : 最长回文子串 时间限制:1000ms 单点时限:1000ms 内存限制:64MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相 ...

  8. 最长回文子串Manacher算法模板

    Manacher算法能够在O(N)的时间复杂度内得到一个字符串以任意位置为中心的回文子串.其算法的基本原理就是利用已知回文串的左半部分来推导右半部分. 首先,在字符串s中,用rad[i]表示第i个字符 ...

  9. 求最长回文子串——Manacher算法

    回文串包括奇数长的和偶数长的,一般求的时候都要分情况讨论,这个算法做了个简单的处理把奇偶情况统一了.算法的基本思路是这样的,把原串每个字符中间用一个串中没出现过的字符分隔开来(统一奇偶),用一个数组p ...

随机推荐

  1. Sentry(v20.12.1) K8S 云原生架构探索,JavaScript 性能监控之采样 Transactions

    系列 Sentry-Go SDK 中文实践指南 一起来刷 Sentry For Go 官方文档之 Enriching Events Snuba:Sentry 新的搜索基础设施(基于 ClickHous ...

  2. Docker 建站小记

    一,前言 Docker 建站小记,我使用了四个镜像来搭建:nginx,certbot,mysql,gradle.欢迎访问:https://www.zzk0.top 这个网页是从 github 上找的个 ...

  3. Java集合List-差集、并集、交集

    Java集合List的差集.并集.交集 转载于:https://www.cnblogs.com/qlqwjy/p/9812919.html 一.List的差集 @Test public void te ...

  4. 【2020CSP-S模拟赛day5】总结

    爆零自闭赛 写在前面 于2022.11.1 这一次题目质量很高(以至于什么都不会) 再一度体验了省选Orz.比赛大体情况,刨去std, wzc神仙230分,比剩下的加起来都高.zyz神仙60分. 其余 ...

  5. 代码托管从业者 Git 指南

    本文作者:李博文 - CODING 后端开发工程师 前言 六七年前,我机缘巧合进入了代码托管行业,做过基于 Git 支持 SVN 客户端接入.Git 代码托管平台分布式.Git 代码托管读写分离.Gi ...

  6. Ajax编程基础

    目录 Ajax编程基础 传统网站中存在的问题 Ajax概述 Ajax的应用场景 Ajax的运行环境 Ajax运行原理及实现 Ajax运行原理 Ajax的实现步骤 1.创建Ajax对象 2.告诉Ajax ...

  7. Linux 通过端口终止进程

    以下命令可用于杀死占用某端口的所有进程. root 用户: kill -9 $(lsof -i tcp:进程号 -t) 非 root 用户: kill -9 $(sudo lsof -i tcp:进程 ...

  8. (006)每日SQL学习:关于to_char函数

    to_char函数的官方文档说明: 详细to_char请移步:https://www.cnblogs.com/reborter/archive/2008/11/28/1343195.html 需求:n ...

  9. automake的简单使用

    https://blog.csdn.net/zhengqijun_/article/details/70105077 xxxxx https://blog.csdn.net/initphp/artic ...

  10. 浅谈正向代理、反向代理和CDN的区别

    一.正向代理 1.正向代理位于客户端和源服务器之间的服务器(代理服务器): 2.隐藏客户端:由代理服务器代替客户端去访问目标服务器,用户需要设置代理服务器的IP和端口: 3.每一次请求是到代理服务器, ...