题解 P3805 【【模板】manacher算法】


我们先看两个字符串:

ABCCBA

ABCDCBA

显然这两字符串是回文的

然而两个串的对称中心的特性不同,第一个串,它的对称中心在两个C中间,然而第二个串,它的对称中心就是D。这样我们如果要记录回文串的对称中心,就显得复杂了。

为了解决这个问题,把两种情况统一起来,我们就在字母之间插入隔板,这样两个问题就统一了,因为所有的对称中心都会有个字符与之对应。像这样

~A|B|C|C|B|A|

(注意到我们这里还插入了一个“~”,原因我们待会说明)

manacher为什么有如此优秀的复杂度呢?让我用比图片还要清楚的文字说明一下

  • 对于一个回文串,有且仅有一个对称中心。且叫它回文对称中心。

  • 在一个回文串,任选一段区间 X ,一定存在关于”回文对称中心“对称的一个区间,且把这个区间叫做关于区间_X_的对称区间。

  • 区间和对称区间一定全等。( 1)(十分显然)

  • 由_(1)得 ,若一个区间的对称区间是回文串,这个区间必定是一个回文串。在大的回文串内,它们回文半径相等。 (2)_

  • 然而我们通过确定关系预先得到的回文半径,它的数值,必定小于等于这个位置真实的回文串半径。(十分显然)

因此,为利用特性(2),我们若记录每个字符作为回文对称中心时的回文串的半径,就可以知道该字符,它,关于某另一个对称中心(称此对称中心对应的回文串叫做“大回文串“)对称的,在大的回文半径内,对应字符的部分回文半径了。

记录这些数据到p数组。同时记录一个mid,一个r,分别代表 已经确定的右侧最靠右的回文串的对称中心和右边界

然而,考虑一些情况,发现我们只能确认我们已知的回文串内的对称关系和回文半径等量关系,关于这个大回文串右侧的世界,我们一无所知。

那么,当我们扫描到一个新的字符时,怎么先确定它的部分回文半径呢?

若当前扫描到的位置为i,若mid<=i<=r,则我们可以找到它的一个对称点。这个点的位置是多少?是mid×2-i。我们现在对其推导一下。

设:这个新点位置为i,它关于mid对称的点为j,将整个字符串看做以下标0位置为原点的数轴,我们由中点公式可得:

( i + j ) / 2 = mid

方程两边同时乘以二, 得:

i + j = 2 * mid

移项, 得:

j=2*mid-i

证毕。

就是这样小学生都会的推导过程,几乎每篇题解都是 直接摆出结论 ,不给出证明和推导,要求学习者自己”找规律” , 我真是不敢苟同。希望大家以后发题解要严谨一点,至少自己没有真正理解就不要发题解,让人觉得manacher是难以学习的算法。至少当我在学习的时候,是一头雾水。

所以,拓展一个新点时,我们不必从这个点左右两边第一个位置开始向两边拓展,可以预先确定一部分回文串。就是因为这个,manacher的复杂度是线性的。

  • 若扩展一个新的关于该字符的回文半径,可以先确定一部分P[i]。

  • 且我们知道,我们能确定的范围,其右侧不得大于r,即:p[i]+i<=r 移项得:p[i] <= r-i

故此,要取一个min 这里给出代码:

	P[i]=min(P[mid*2-i],r-i)

最终答案是

	P(max)-1

讲到这里,应该十分清楚了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=11000002;
char data[maxn<<1];
int p[maxn<<1];
int cnt=1;
#define RP(t,a,b) for(int t=(a),edd=(b);t<=edd;t++)
inline void qr(){
char c=getchar();
data[0]='~';
data[1]='|';
while(c<'a'||c>'z')
c=getchar();
while(c>='a'&&c<='z'){
data[++cnt]=c;
data[++cnt]='|';
c=getchar();
}
return;
}
int maxans,rb,mid;
inline int cmpless(int x,int y){
if(x<y)
return x;
return y;
}
int main(){
//freopen("in.in","r",stdin);
//freopen("out.out","w",stdout);
qr();
RP(t,1,cnt){
if(t<rb)
p[t]=cmpless(p[(mid<<1)-t],rb-t);
else
p[t]=1;
while(data[t-p[t]]==data[t+p[t]])
//暴力拓展左右两侧,当t-p[t]==0时,由于data[0]是'~',自动停止。
//故不会下标溢出。
p[t]++;
if(p[t]+t>rb)
//更新mid和rb,保持rb是最右的才能保证我们提前确定的部分回文半径尽量多。
rb=p[t]+t,mid=t;
if(p[t]>maxans)
maxans=p[t];
}
cout<<maxans-1<<endl;
return 0;
}

祝大家学习愉快

题解 P3805 【【模板】manacher算法】的更多相关文章

  1. 洛谷P3805 [模板]Manacher算法 [manacher]

    题目传送门 题目描述 给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度. 字符串长度为n 输入输出格式 输入格式: 一行小写英文字符a,b,c...y,z组成的字符 ...

  2. 模板 manacher算法

    题目描述 给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度. 字符串长度为n 输入输出格式 输入格式: 一行小写英文字符a,b,c...y,z组成的字符串S 输出格 ...

  3. 洛谷.3805.[模板]manacher算法

    题目链接 之前做很早了没写这篇,补上. 记录当前ex[]最大的回文中心id和最远延伸范围mx! 关于串的构造: 应该是 @ #A#B#C#B#A# $ ,而不是 @ A#B#C#B#A $ 比如 @a ...

  4. 洛谷 P3805 【模板】manacher算法

    洛谷 P3805 [模板]manacher算法 洛谷传送门 题目描述 给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度. 字符串长度为n 输入格式 一行小写英文字符 ...

  5. [洛谷P3805]【模板】manacher算法

    题目大意:给你一个字符串,求出它的最长回文字段 题解:$manacher$算法 卡点:$p$数组未开两倍空间 C++ Code: #include <cstdio> #include &l ...

  6. Manacher || Luogu P3805【模板】manacher算法

    题面:[模板]manacher算法 代码: #include<cstdio> #include<cstring> #include<iostream> #defin ...

  7. 洛谷 P3805【模板】manacher算法

    题目链接:https://www.luogu.com.cn/problem/P3805 Manacher算法$O(n)$: 求以每个字符为中心的最长回文串的半径:如果要求可以以字符间隙为回文中心,就要 ...

  8. 【洛谷 P3805】 【模板】manacher算法

    题目链接 manacher算法:在线性时间内求一个字符串中所有/最长回文串的算法. 先来考虑一下暴力的算法,枚举每个中点,向两边扩展,时间复杂度\(O(n^2)\). 来分析下此算法的缺点. 1.因为 ...

  9. P3805 【模版】manacher算法(manacher)

    P3805 [模版]manacher算法 题目描述 给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度. 字符串长度为n 输入输出格式 输入格式: 一行小写英文字符a ...

随机推荐

  1. hosts不支持泛解析

    hosts不支持泛解析,只能是一个域名对应一个IP. 如果想要实现只能用一些第三方的DNS软件做解析.

  2. shell实践--shell内嵌指令实现查看上线时间

    实践小点子: 1. 做一个shell 内嵌指令例如:ls,cd,pwd这样     就实现查看上线多久:   解决办法有两种: 1) 利用脚本,如新指令为look;利用别名的方法,将look.sh脚本 ...

  3. Linux基础学习5

    程序管理与SELinux初探 process&program  程序 (program):通常为 binary program ,放置在储存媒体中 (如硬盘.光盘.软盘.磁带等), 为实体档案 ...

  4. IOS开发者账号的相关配置 - 接受邀请后的步骤

    说明: 1.本文主要针对企业账户, 并假定主账号已经申请到了. 2.账号类型分为3种:Agent(创建者),Admin(管理员)及Member(成员) 一. 1.申请子账号 使用Agent或Admin ...

  5. XCODE 4.5 IOS多语言设置 及NSLocalizedString和NSLocalizedStringFromTable的用法。

    前 些天升级到Xcode4.5,现在正在用Xcode4.5+IOS6开发项目,当使用国际化时,遇到了一点问题,之前版本Xcode上新建 Localizable.strings后,添加语言的“+”号不见 ...

  6. 【redis】java操作redis时,StringRedisTemplate的expire()方法的作用,什么时候使用

    java操作redis时,StringRedisTemplate的expire()方法的作用,什么时候使用 //重新设置过期时间为30分钟,刷新时间 redisTemplate.expire(MsOp ...

  7. PHP设置头部编码为UTF-8语句

    header("Content-type: text/html; charset=utf-8");

  8. Mac. 文件夹赋予权限

    1. click on your background to go to finder click on go and go to folder /usr right click on local a ...

  9. 【GLSL教程】(四)shder的简单示例 【转】

    http://blog.csdn.net/racehorse/article/details/6638455 GLSL的Hello World 这一节中包含一个最基本的shader,它提供如下功能:顶 ...

  10. 2016.10.18kubernetes 的8080和6443端口的区别与联系

    由看过的资料知道,可以使用kubectl,client libraries和REST请求来访问api.   来自官方资料: By default the Kubernetes APIserver se ...