BZOJ2342:[SHOI2011]双倍回文
浅谈\(Manacher\):https://www.cnblogs.com/AKMer/p/10431603.html
题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=2342
假设我已经将原字符串的\(p\)数组求好了。
双倍回文肯定是#\(W\)#\(W^R\)#\(W\)#\(W^R\)#
我们枚举中间一个#,求在它的回文半径的一半以内最靠前的第一个满足\(i+p_i-1\geqslant pos\)的第一个#,那么肯定第三个#也是存在的,然后用这个更新答案即可。
怎么快速找到第一个#呢?并查基优化即可。如果一个位置的\(i+p_i-1< pos\)那么显然这个位置也不可能作为后面的#号的第一个#,直接用并查基把他和下一个位置合起来即可。
时间复杂度:\(O(\alpha n)\)
空间复杂度:\(O(n)\)
代码如下:
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=1e6+5;
int n,ans;
char s[maxn];
int p[maxn],fa[maxn];
int read() {
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
return x*f;
}
int find(int x) {
if(fa[x]==x)return x;
return fa[x]=find(fa[x]);
}
int main() {
n=read();
scanf("%s",s+1);
for(int i=n;i;i--)
s[i<<1]=s[i],s[(i<<1)-1]='#';
s[n<<1|1]='#',s[0]='$',n=n<<1|1;
int id=0,mx=0;
for(int i=1;i<=n;i+=2)fa[i]=i;
for(int i=1;i<=n;i++) {
p[i]=i<=mx?min(mx-i+1,p[(id<<1)-i]):1;
while(s[i-p[i]]==s[i+p[i]])p[i]++;
if(i+p[i]-1>mx)id=i,mx=i+p[i]-1;
if(s[i]=='#'&&p[i]>=5) {
int st=i-p[i]/2;if(st%2==0)st|=1;
for(int j=find(st);j<=i;j=find(j+2))
if(j+p[j]<=i) fa[j]=find(j+2);
else {ans=max(ans,(i-j)<<1);break;}
}
}
printf("%d\n",ans);
return 0;
}
根据洛谷一大佬的题解,此题有\(O(n)\)做法,只需要在\(mx\)被更新的时候枚举旧的\(mx\)到新的\(mx\)之间的点做双倍回文的右端点,新的\(id\)作为双倍回文的中点,然后判断对称过去是不是个回文即可。这样子做双倍回文肯定会被枚举到,并且枚举的总时间就是\(mx\)的改变量。
时间复杂度:\(O(n)\)
空间复杂度:\(O(n)\)
代码如下:
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=1e6+5;
int n,ans;
int p[maxn];
char s[maxn];
int read() {
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
return x*f;
}
int main() {
n=read();
scanf("%s",s+1);
for(int i=n;i;i--)
s[i<<1]=s[i],s[(i<<1)-1]='#';
s[n<<1|1]='#',s[0]='$',n=n<<1|1;
int id=0,mx=0;
for(int i=1;i<=n;i++) {
p[i]=i<=mx?min(mx-i+1,p[(id<<1)-i]):1;
while(s[i-p[i]]==s[i+p[i]])p[i]++;
if(i+p[i]-1>mx) {
if(s[i]=='#') {
int st=mx+1;if(s[st]!='#')st++;
for(int j=st;j<=i+p[i]-1;j+=2) {
int pos=i+(j-i)/2;pos=(i<<1)-pos;
if(s[pos]=='#'&&p[pos]+pos-1>=i)ans=max(ans,j-i);
}
}
id=i,mx=i+p[i]-1;
}
}
printf("%d\n",ans);
return 0;
}
BZOJ2342:[SHOI2011]双倍回文的更多相关文章
- BZOJ2342 Shoi2011 双倍回文 【Manacher】
BZOJ2342 Shoi2011 双倍回文 Description Input 输入分为两行,第一行为一个整数,表示字符串的长度,第二行有个连续的小写的英文字符,表示字符串的内容. Output 输 ...
- BZOJ2342: [Shoi2011]双倍回文
2342: [Shoi2011]双倍回文 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 923 Solved: 317[Submit][Status ...
- BZOJ2342[Shoi2011]双倍回文——回文自动机
题目描述 输入 输入分为两行,第一行为一个整数,表示字符串的长度,第二行有个连续的小写的英文字符,表示字符串的内容. 输出 输出文件只有一行,即:输入数据中字符串的最长双倍回文子串的长度,如果双倍回文 ...
- bzoj千题计划306:bzoj2342: [Shoi2011]双倍回文 (回文自动机)
https://www.lydsy.com/JudgeOnline/problem.php?id=2342 解法一: 对原串构建回文自动机 抽离fail树,从根开始dfs 设len[x]表示节点x表示 ...
- BZOJ2342:[SHOI2011]双倍回文(Manacher)
Description Input 输入分为两行,第一行为一个整数,表示字符串的长度,第二行有个连续的小写的英文字符,表示字符串的内容. Output 输出文件只有一行,即:输入数据中字符串的最长 ...
- [BZOJ2342] [Shoi2011]双倍回文(manacher)
传送门 manacher...... 先跑一边manacher是必须的 然后枚举双倍回文串的对称轴x 把这个双倍回文串分成4段,w wR w wR 发现,只有当 y <= x + p[x] / ...
- bzoj2342: [Shoi2011]双倍回文 pam
题解:先建pam,然后在fail树上dfs,从上到下的链如果有当前长度最远回文串的一半,那么更新答案 //#pragma GCC optimize(2) //#pragma GCC optimize( ...
- 【BZOJ-2342】双倍回文 Manacher + 并查集
2342: [Shoi2011]双倍回文 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1799 Solved: 671[Submit][Statu ...
- 【BZOJ2342】双倍回文(回文树)
[BZOJ2342]双倍回文(回文树) 题面 BZOJ 题解 构建出回文树之后 在\(fail\)树上进行\(dp\) 如果一个点代表的回文串长度为\(4\)的倍数 并且存在长度为它的一半的回文后缀 ...
- 2018.06.30 BZOJ 2342: [Shoi2011]双倍回文(manacher)
2342: [Shoi2011]双倍回文 Time Limit: 10 Sec Memory Limit: 128 MB Description Input 输入分为两行,第一行为一个整数,表示字符串 ...
随机推荐
- Java中ArrayList和LinkedList区别、ArrayList和Vector的区别
一般大家都知道ArrayList和LinkedList的大致区别: 1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构. 2.对于随机访问get和set,Ar ...
- ubuntu 12.04.2 基于 L3.0.35_1.1.0_121218_source LTIB 问题汇总
1)解压L3.0.35_1.1.0_121218_source.tar.gz 2)cd L3.0.35_1.1.0_121218_source ,执行./install 3) 复制 patch-l ...
- 前端之CSS样式
一.CSS 1.什么是CSS 层叠样式表(英文全称:Cascading Style Sheets)是一种用来表现HTML(标准通用标记语言的一个应用)或XML(标准通用标记语言的一个子集)等文件样式的 ...
- Centos系统 上下文切换的检查思路
1.什么是上下文切换(Context Switch)? 上下文切换,有时也称做进程切换或任务切换,是指CPU从一个进程或线程切换到另一个进程或线程. 操作系统可以同时运行多个进程, 然而一颗CPU同时 ...
- 20145240 《Java程序设计》第五次实验报告
20145240 <Java程序设计>第五次实验报告 北京电子科技学院(BESTI)实验报告 课程:Java程序设计 班级:1452 指导教师:娄嘉鹏 实验日期:2016.05.06 实验 ...
- Java final static关键字
Java中的final关键字 使用 final 关键字做标识有 “最终的” 含义. final 可以修饰 类.方法.属性.变量 final 修饰类: 则该类不允许被继承 final 修饰方法:则该方法 ...
- python机器学习——分词
使用jieba库进行分词 安装jieba就不说了,自行百度! import jieba 将标题分词,并转为list seg_list = list(jieba.cut(result.get(" ...
- Java 创建线程的两种方法
Java提供了线程类Thread来创建多线程的程序.其实,创建线程与创建普通的类的对象的操作是一样的,而线程就是Thread类或其子类的实例对象.每个Thread对象描述了一个单独的线程.要产生一个线 ...
- NovaException: Unexpected vif_type=binding_failed
nova/virt/libvirt/vif.py: _("Unexpected vif_type=%s") % vif_type) NovaException: Unexpecte ...
- ROS配置C++14环境
ROS配置C++11环境 打开Terminal, sudo vim /opt/ros/<yourversion>/share/catkin/cmake/toplevel.cmake 比如: ...