BZOJ.2565.[国家集训队]最长双回文串(Manacher/回文树)
求给定串的最长双回文串。
\(n\leq10^5\)。
Manacher:
记\(R_i\)表示以\(i\)位置为结尾的最长回文串长度,\(L_i\)表示以\(i\)开头的最长回文串长度。答案就是\(\max\{R_i+L_{i+1}\}\)。式子可能会有差别,因为Manacher会在里面加字符。当然我们直接只用'#'位置的\(L_i+R_i\)就可以更新答案啦。
Manacher,然后对于位置\(i\),设它的最远延伸距离是\(ex_i\)。
然后用\(i-j\)更新\(L_j,\;j\in[i-ex_i+1,\ i]\),\(j-i\)更新\(R_j,\;j\in[i,i+ex_i-1]\)(注意字符串里加了'#'的)。
显然只需要维护更新它的\(\max\{i\},\min\{i\}\)就可以了,可以用线段树。(当然我懒得去写)
但这个做法显然不够好,考虑能否\(O(1)\)更新,也就是更新一个位置然后递推过来。(表示没想出来= =)
可以啊...更新\(i-j\)这个值,相邻两个位置是相差\(1\)的。而从\(j\)更新到\(i\)位置时,\(i-j\)就\(\leq0\)了。也就是我们只更新\(L_{i-ex_i+1}=\max\{ex_i-1\}\),最后用\(L_j=\max\{L_j,\ L_{j-1}-1\}\)递推即可。\(R\)同理。
这样就\(O(n)\)啦。
当然我们只需要在'#'处统计,所以只更新这些位置即可。
回文树:
对正反串建两次回文树,求出每个位置的最长回文长度就可以了。。
Manacher:
//3360kb 84ms
#include <cstdio>
#include <cstring>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=2e5+5;
int ex[N],L[N],R[N];
char s[N];
int Build()
{
int n=strlen(s+1);
for(int i=n; i; --i) s[i<<1]=s[i], s[(i<<1)-1]='#';
s[0]='!', s[n<<1|1]='#', s[(n<<1)+2]='@';
return n<<1|1;
}
void Manacher(const int n)
{
for(int i=1,id,mx=0; i<=n; ++i)
{
if(i<mx) ex[i]=std::min(ex[2*id-i],mx-i);
else ex[i]=1;
while(s[i-ex[i]]==s[i+ex[i]]) ++ex[i];
if(i+ex[i]>mx) mx=i+ex[i], id=i;
L[i-ex[i]+1]=std::max(L[i-ex[i]+1],ex[i]-1);
R[i+ex[i]-1]=std::max(R[i+ex[i]-1],ex[i]-1);
}
}
int main()
{
scanf("%s",s+1);
int n=Build(); Manacher(n);
for(int i=n-2; i>=1; i-=2) R[i]=std::max(R[i],R[i+2]-2);//i>=1 not i!
for(int i=3; i<=n; i+=2) L[i]=std::max(L[i],L[i-2]-2);
int ans=0;
for(int i=1; i<=n; i+=2) if(L[i]&&R[i]) ans=std::max(ans,L[i]+R[i]);//if!
printf("%d\n",ans);
return 0;
}
回文树:
//23576kb 156ms
#include <cstdio>
#include <cstring>
#include <algorithm>
typedef long long LL;
const int N=1e5+5;
char s[N];
struct PAM
{
int las,tot,fail[N],son[N][26],len[N],mx[N];
PAM() {tot=1, fail[0]=1, len[1]=-1;}
inline int Find(int x,int n)
{
while(s[n-len[x]-1]!=s[n]) x=fail[x];
return x;
}
void Insert(int c,int n)
{
int p=Find(las,n);
if(!son[p][c])
{
int np=++tot; fail[np]=son[Find(fail[p],n)][c];
son[p][c]=np, len[np]=len[p]+2;
}
las=son[p][c];
}
void Build(char *s,const int n,const int opt)
{
if(opt) for(int i=1; i<=n; ++i) Insert(s[i]-'a',i), mx[i]=len[las];
else for(int i=1; i<=n; ++i) Insert(s[i]-'a',i), mx[n-i+1]=len[las];//...
}
}p1,p2;
int main()
{
scanf("%s",s+1); int n=strlen(s+1);
p1.Build(s,n,1), std::reverse(s+1,s+1+n), p2.Build(s,n,0);
int ans=0;
for(int i=1; i<n; ++i) ans=std::max(ans,p1.mx[i]+p2.mx[i+1]);
printf("%d\n",ans);
return 0;
}
BZOJ.2565.[国家集训队]最长双回文串(Manacher/回文树)的更多相关文章
- Manacher || P4555 [国家集训队]最长双回文串 || BZOJ 2565: 最长双回文串
题面:P4555 [国家集训队]最长双回文串 题解:就.就考察马拉车的理解 在原始马拉车的基础上多维护个P[i].Q[i]数组,分别表示以i结尾最长回文子串的长度和以i开头的最长回文子串的长度 然后就 ...
- P4555 [国家集训队]最长双回文串
P4555 [国家集训队]最长双回文串 manacher 用manacher在处理时顺便把以某点开头/结尾的最长回文串的长度也处理掉. 然后枚举. #include<iostream> # ...
- 洛谷 P4555 [国家集训队]最长双回文串 解题报告
P4555 [国家集训队]最长双回文串 题目描述 顺序和逆序读起来完全一样的串叫做回文串.比如acbca是回文串,而abc不是(abc的顺序为abc,逆序为cba,不相同). 输入长度为\(n\)的串 ...
- 【洛谷】P4555 [国家集训队]最长双回文串
P4555 [国家集训队]最长双回文串 题源:https://www.luogu.com.cn/problem/P4555 原理:Manacher 还真比KMP好理解 解决最长回文串问题 转化为长度为 ...
- Manacher【p4555】 [国家集训队]最长双回文串
题目描述 顺序和逆序读起来完全一样的串叫做回文串.比如acbca是回文串,而abc不是(abc的顺序为abc,逆序为cba,不相同). 输入长度为 n 的串 S ,求 S 的最长双回文子串 T ,即可 ...
- [国家集训队]最长双回文串 manacher
---题面--- 题解: 首先有一个直观的想法,如果我们可以求出对于位置i的最长后缀回文串和最长前缀回文串,那么我们枚举分界点然后合并前缀和后缀不就可以得到答案了么? 所以我们的目标就是求出这两个数列 ...
- P4555 [国家集训队]最长双回文串(回文树)
题目描述 顺序和逆序读起来完全一样的串叫做回文串.比如acbca是回文串,而abc不是(abc的顺序为abc,逆序为cba,不相同). 输入长度为 n 的串 S ,求 S 的最长双回文子串 T ,即可 ...
- luoguP4555 [国家集训队]最长双回文串 manacher算法
不算很难的一道题吧.... 很容易想到枚举断点,之后需要处理出以$i$为开头的最长回文串的长度和以$i$为结尾的最长回文串的长度 分别记为$L[i]$和$R[i]$ 由于求$R[i]$相当于把$L[i ...
- 【洛谷 P4555】 [国家集训队]最长双回文串 (Manacher)
题目链接 \(|S|<=10^5\),时间还是很宽松的. 允许我们使用线性/\(N\log N\)/甚至\(N \sqrt N\)的算法. 设\(l[i]\)表示以\(a[i]\)结尾的最长回文 ...
随机推荐
- mysql 去除重复 Select中DISTINCT关键字的用法 在使用mysql时,有时需要查询出某个字段不重复的记录,虽然mysql提供 有distinct这个关键字来过滤掉多余的重复记录只保留一条,但往往只用它来返回不重复记录的条数,而不是用它来返回不重记录的所有值。其原因是 distinct只能返回它的目标字段,而无法返回其它字段,这个问题让我困扰了很久,用distinct不能解决的话,
在使用mysql时,有时需要查询出某个字段不重复的记录,虽然mysql提供 有distinct这个关键字来过滤掉多余的重复记录只保留一条,但往往只用它来返回不重复记录的条数,而不是用它来返回不重记 ...
- C++ Primer 笔记——动态数组
1.动态数组定义时也需要指明数组的大小,但是可以不是常量. int i; int arr[i]; // 错误,数组的大小必须为常量 int *p = new int[i]; // 正确,大小不必是常量 ...
- sqoop的基本语法详解及可能遇到的错误
1 sqoop介绍 Apache Sqoop是专为Apache Hadoop和结构化数据存储如关系数据库之间的数据转换工具的有效工具.你可以使用Sqoop从外部结构化数据存储的数据导入到Hadoop分 ...
- Python函数系列之eval()
1.作用:将字符串str当成有效的表达式来求值并返回计算结果. 2.语法:eval(source[, globals[, locals]]) 3.说明:参数:source:一个Python表达式或函 ...
- vue 引入Element组件
1.打开cmd,在当前目录中运行: npm i element-ui -S 2.src/main.js(红色的) import Vue from 'vue' import App from './Ap ...
- 金蝶开k3wise客户端访问中间层不时提示远程服务器不存在或不可用
此问题一般是发生在WIN7客户端+中间层为WIN2008 R2系统,一般是中间层启用guest引起: a. 从Windows Vista以后, Negotiate (http://msdn.micro ...
- 如何下载kubenetes最新的rpm包?
一,新增aliyun的软件仓库 tee /etc/yum.repos.d/kubernetes.repo <<-'EOF' [kubernetes] name=Kubernetes bas ...
- java解析html的table
import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org. ...
- 萨塔尼亚的期末考试(fail)
题解: 这题比较妙啊... 首先暴力自己算是找不出规律的 有一种直觉就是可以把式子变成{f[1]+...f[n]}+{f[2]+...+f[n]}+{f[3]+...+f[n]}... 然后看了题解发 ...
- 初识Python.day2
一. python运算符 算数运算符 比较运算符 赋值运算符 逻辑运算符 成员运算符 二. Python基础数据类型 1. python字符串总结 # 首字母变大写 # a1 = "jiu ...