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]\)结尾的最长回文 ...
随机推荐
- [Gym-102091E] How Many Groups
/* 先将a数组从小到大排序 dp[i][j][k]表示到以第i个数为结尾的,且第i个数改了j次,第i个数改动值为k-1的集合最大值 ans是dp过程中的最大集合大小 状态转移: 首先是到i改动为0次 ...
- hdu2196 树形dp经典|树的直径
/* 两种做法 1.求出树直径v1,v2,那么有一个性质:任取一点u,树上到u距离最远的点必定是v1或v2 那么可以一次dfs求树v1 第二次求dis1[],求出所有点到v1的距离,同时求出v2 第三 ...
- CF979E
非常好的dp,非常考dp的能力 很显然是个计数问题,那么很显然要么是排列组合,要么是递推,这道题很显然递推的面更大一些. 那么我们来设计一下状态: 设状态f[i][j][k][p]表示目前到了第i个点 ...
- Axure实现多用户注册验证
*****多用户登录验证***** 一.(常规想法)方法:工作量较大,做起来繁琐 1.当用户名和密码相同时怎么区分两者,使用冒号和括号来区分: eg. (admin:123456)(123456:de ...
- mysql5.7 root用户默认密码
1. 查找密码 Mysql 5.7 在自动初始化数据库的时候,会生成root用户的默认密码. 通过 grep "temporary password" /var/log/mysql ...
- ThreadLocal用法详解和原理
一.用法 ThreadLocal用于保存某个线程共享变量:对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量. 1.Thr ...
- SCSS 使用@each 方法循环遍历数组颜色并给li赋值
$list-bg:red,orange,blue,skyblue; ul{ >li{ height: 30px; @each $c in $list-bg{ $i:index($list-bg, ...
- centos/redhat破解账号密码
说明:1.个人觉得centos系统和redhat系统差不多,界面都差不多一样. 2.下面方法用于开机root密码忘了,其他人篡改root密码等等 下面是破解账号密码(图解) 之后要等久点 效果: 方法 ...
- VS2015 生成事件 命令参数
来源:https://stackoverflow.com/questions/11001822/copy-files-from-one-project-to-another-using-post-bu ...
- 利用sqlmap注入测试
安装:yum install -y gitcd /usr/local && git clone https://github.com/sqlmapproject/sqlmap.gitc ...