题解 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. ActiveMQ spring (一)

    在5.8.0版本下 配置成功. 参考文档:http://yinbinhome.iteye.com/blog/1273228

  2. 牛客网暑期ACM多校训练营 记录

    所有牛客多校的做题记录请右转队伍wiki Name Date Rank 2018 Multi-University,Nowcoder Day 1 2018.7.19 16/338 5/10 https ...

  3. 实现一个Java五子棋

    五子棋手把手教你写: 写在前面的话: 回想起从前初学代码的五子棋简直写的不像样子.今天闲来无事就写了个五子棋的小程序. 一来呢回忆一下很久以前写代码时的感觉. 二来呢顺便帮下诸位有需求的学生,顺利的C ...

  4. [LeetCode] 1.Two Sum 两数之和分析以及实现 (golang)

    题目描述: /* Given an array of integers, return indices of the two numbers such that they add up to a sp ...

  5. schema设计

    Schema设计   Schema:表的模式:   设计数据的表,索引,以及表和表的关系 在数据建模的基础上将关系模型转为数据库表 满足业务模型需要基础上根据数据库和应用特点优化表结构   关系模型图 ...

  6. Jsp2.0自定义标签(第一天)——一个简单的例子

    今天是学习自定义标签的第一天 Jsp2.0以来,自定义标签的实现比传统标签的实现容易了很多,一般只要extends类SimpleSupport重写doTag()方法即可. 先看最简单的例子,输出一个H ...

  7. mac下virtualenv使用

    1  sudo pip install virtualenv 安装 2 找一合适目录装虚拟环境 virtualenv virzhongguo 3  激活虚拟环境 source virzhongguo/ ...

  8. DotnetBrowser入门教程-(3)启动与使用简单的WebSocket服务

    websocket是个很好的通信协议,基本可以贯穿支持html5的所有设备.dotnetbrowser内置了对websocket服务端与客户端的支持.请看例子: 1.新建桌面项目,基于.net 4.0 ...

  9. Notification(二)——PendingIntent的flag导致数据同样的问题

    MainActivity例如以下: package cc.cu; import android.os.Bundle; import android.view.View; import android. ...

  10. mysql数据库解决中文乱码问题

    安装mysql之后.假设存储中文.再读出的时候就会出现乱码问题. 如今的字符集有几百种之多,都是一些公司或者组织定义的. 我们应该使用可以容纳世界所有语言所有字符的字符集,这样就不会再出现乱码问题. ...