bzoj千题计划306:bzoj2342: [Shoi2011]双倍回文 (回文自动机)
https://www.lydsy.com/JudgeOnline/problem.php?id=2342
解法一:
对原串构建回文自动机
抽离fail树,从根开始dfs
设len[x]表示节点x表示的最长回文子串长度
在fail树上,x到根节点的路径上的点表示的字符串包含了x代表的回文子串的所有回文后缀/前缀
所以若dfs到了x,若len[x]为偶数,标记len[x]*2,如果在x的子树中能找到len为len[x]*2的点,那么len[x]*2*2就可以用来更新答案
#include<cstdio>
#include<algorithm> using namespace std; #define N 500001 char ss[N+];
int s[N+]; int tot=,last;
int len[N],fail[N],tr[N][];
int p,c,np,t; bool ok[N<<];
int ans; int front[N],to[N],nxt[N],cnt; void add(int u,int v)
{
to[++cnt]=v; nxt[cnt]=front[u]; front[u]=cnt;
} void extend(int i)
{
p=last; c=s[i];
while(s[i--len[p]]!=c) p=fail[p];
if(!tr[p][c])
{
np=++tot;
len[np]=len[p]+;
t=fail[p];
while(s[i--len[t]]!=c) t=fail[t];
fail[np]=tr[t][c];
add(fail[np],np);
tr[p][c]=np;
}
else np=tr[p][c];
last=np;
} void dfs(int x)
{
if(ok[len[x]]) ans=max(ans,len[x]);
if(!(len[x]&)) ok[len[x]<<]=true;
for(int i=front[x];i;i=nxt[i]) dfs(to[i]);
if(!(len[x]&)) ok[len[x]<<]=false;
} int main()
{
int n;
scanf("%d",&n);
scanf("%s",ss+);
s[]=-;
for(int i=;i<=n;++i) s[i]=ss[i]-'a';
fail[]=;
len[]=-;
for(int i=;i<=n;++i) extend(i);
dfs();
printf("%d",ans);
}
解法二:
原串和其反串拼接,中间用两个不一样的字符隔开
然后构建回文自动机
考虑一个双倍回文的分割点i和i+1
i是前缀回文的结束位置
i+1是后缀回文的开始位置
设以i为结束位置的最长回文子串为s1,在回文自动机上的节点为a
设以i+1开始位置的最长回文子串为s2,在回文自动机上的节点为b
设前缀以i结束,后缀以i+1开始的双倍回文子串的一半为s,长度为L
那么现在有两个要求:
1、L为偶数
2、s是s1的后缀,s是s2的前缀,且s最长
对于要求2,因为开始原串和反串拼接构建了回文自动机,所以就是求a和b在fail树上的LCA
对于要求1,对每个点x记录fail树上 x的祖先中离它最近的长度为偶数的回文串即可
倍增求LCA会超时
不会tarjan求LCA(~~~~(>_<)~~~~)
树链剖分求LCA 会被卡空间
最后还是选了树剖。。。
回文自动机用了map存储,
注意回文自动机中有节点0,在树剖第二遍dfs的时候,重儿子初始化的编号不能是0
#include<map>
#include<cmath>
#include<cstdio>
#include<algorithm> using namespace std; #define N 1000001 int n,m;
char ss[N+];
int s[N+]; int tot=,last;
int len[N],fail[N];
map<int,int>tr[N];
int p,c,np,t; int use[N],pos[N]; int front[N],to[N],nxt[N],cnt; int bl[N],dep[N],siz[N],fa[N]; void add(int u,int v)
{
to[++cnt]=v; nxt[cnt]=front[u]; front[u]=cnt;
} void extend(int i)
{
p=last; c=s[i];
while(s[i--len[p]]!=c) p=fail[p];
if(!tr[p][c])
{
np=++tot;
len[np]=len[p]+;
t=fail[p];
while(s[i--len[t]]!=c) t=fail[t];
fail[np]=tr[t][c];
add(fail[np],np);
use[np]=len[np]& ? use[fail[np]] : np;
tr[p][c]=np;
}
else np=tr[p][c];
last=np;
pos[i]=np;
} void build()
{
s[]=-;
for(int i=;i<=n;++i) s[i]=ss[i]-'a';
m=n;
s[++m]=; s[++m]=;
for(int i=n;i;--i) s[++m]=ss[i]-'a';
fail[]=;
len[]=-;
for(int i=;i<=m;++i) extend(i);
} void dfs1(int x)
{
siz[x]=;
for(int i=front[x];i;i=nxt[i])
{
dep[to[i]]=dep[x]+;
fa[to[i]]=x;
dfs1(to[i]);
siz[x]+=siz[to[i]];
}
} void dfs2(int x,int top)
{
int y=-;
bl[x]=top;
for(int i=front[x];i;i=nxt[i])
if(y==- || siz[to[i]]>siz[y]) y=to[i];
if(y==-) return;
dfs2(y,top);
for(int i=front[x];i;i=nxt[i])
if(to[i]!=y) dfs2(to[i],to[i]);
} int get_lca(int u,int v)
{
while(bl[u]!=bl[v])
{
if(dep[bl[u]]<dep[bl[v]]) swap(u,v);
u=fa[bl[u]];
}
return dep[u]<dep[v] ? u : v;
} void solve()
{
add(,);
dfs1();
dfs2(,);
int ans=;
int lca;
for(int i=;i<=n;++i)
{
lca=get_lca(pos[i],pos[m-i]);
// printf("%d %d\n",pos[i],pos[m-i]);
ans=max(ans,len[use[lca]]<<);
//printf("%d\n",ans);
}
printf("%d",ans);
} int main()
{
scanf("%d",&n);
scanf("%s",ss+);
build();
solve();
}
bzoj千题计划306:bzoj2342: [Shoi2011]双倍回文 (回文自动机)的更多相关文章
- bzoj千题计划305:bzoj2565: 最长双回文串(回文自动机)
https://www.lydsy.com/JudgeOnline/problem.php?id=2565 正着构造回文自动机 倒过来再构造一个回文自动机 分别求出以位置i开始的和结尾的最长回文串 # ...
- bzoj千题计划300:bzoj4823: [Cqoi2017]老C的方块
http://www.lydsy.com/JudgeOnline/problem.php?id=4823 讨厌的形状就是四联通图 且左右各连一个方块 那么破坏所有满足条件的四联通就好了 按上图方式染色 ...
- bzoj千题计划196:bzoj4826: [Hnoi2017]影魔
http://www.lydsy.com/JudgeOnline/problem.php?id=4826 吐槽一下bzoj这道题的排版是真丑... 我还是粘洛谷的题面吧... 提供p1的攻击力:i,j ...
- bzoj千题计划280:bzoj4592: [Shoi2015]脑洞治疗仪
http://www.lydsy.com/JudgeOnline/problem.php?id=4592 注意操作1 先挖再补,就是补的范围可以包含挖的范围 SHOI2015 的题 略水啊(逃) #i ...
- bzoj千题计划177:bzoj1858: [Scoi2010]序列操作
http://www.lydsy.com/JudgeOnline/problem.php?id=1858 2018 自己写的第1题,一遍过 ^_^ 元旦快乐 #include<cstdio> ...
- bzoj千题计划317:bzoj4650: [Noi2016]优秀的拆分(后缀数组+差分)
https://www.lydsy.com/JudgeOnline/problem.php?id=4650 如果能够预处理出 suf[i] 以i结尾的形式为AA的子串个数 pre[i] 以i开头的形式 ...
- bzoj千题计划304:bzoj3676: [Apio2014]回文串(回文自动机)
https://www.lydsy.com/JudgeOnline/problem.php?id=3676 回文自动机模板题 4年前的APIO如今竟沦为模板,,,╮(╯▽╰)╭,唉 #include& ...
- bzoj千题计划292:bzoj2244: [SDOI2011]拦截导弹
http://www.lydsy.com/JudgeOnline/problem.php?id=2244 每枚导弹成功拦截的概率 = 包含它的最长上升子序列个数/最长上升子序列总个数 pre_len ...
- bzoj千题计划278:bzoj4590: [Shoi2015]自动刷题机
http://www.lydsy.com/JudgeOnline/problem.php?id=4590 二分 这么道水题 没long long WA了两发,没判-1WA了一发,二分写错WA了一发 最 ...
随机推荐
- Activiti的部署问题
http://www.kafeitu.me/activiti/2012/03/22/workflow-activiti-action.html 既可以通过每次Spring应用程序启动时,执行部署命令. ...
- Java使用HTTPClient3.0.1开发的公众平台消息模板的推送功能
package com.company.product.manager.busniess.impl; import java.io.IOException;import java.nio.charse ...
- Oracle 导入导出报错的简单处理
这边出现报错: 简单查了下资料发现: https://blog.csdn.net/lichkui/article/details/5489708 在imp 的命令后面 增加buffer 即可 比如 i ...
- ping命令返回的TTL值判断操作系统
通过简单的ping命令,查看返回的TTL值来判断对方的操作系统 生存时间(TTL)是IP分组中的一个值,网络中的路由器通过察看这个值就可以判断这个IP分组是不是已经在网络中停留了很久,进而决定是否要将 ...
- Angular 行内式依赖注入
var app = angular.module('myApp', ['ng']); //创建一个自定义服务app.factory('$Debug', function () { return { d ...
- jmeter创建高级测试计划
如果应用程序使用重写地址而不是使用cookie存储信息,需要做一些额外的工作去测试程序 为了正确的响应重写地址,jmeter 需要解析 从服务器获取html 并且检索会话ID, 1 合理利用pre-p ...
- SpringMVC @SessionAttributes 使用
@SessionAttributes 只能作用在类上,作用是将指定的Model中的键值对添加至session中,方便在下一次请求中使用. 简单示例 目标是通过 @SessionAttributes 注 ...
- codeforces 777C
C.Alyona and Spreadsheet During the lesson small girl Alyona works with one famous spreadsheet compu ...
- Educational Codeforces Round 26 B,C
B. Flag of Berland 链接:http://codeforces.com/contest/837/problem/B 思路:题目要求判断三个字母是否是条纹型的,而且宽和高相同,那么先求出 ...
- LCT总结——概念篇+洛谷P3690[模板]Link Cut Tree(动态树)(LCT,Splay)
为了优化体验(其实是强迫症),蒟蒻把总结拆成了两篇,方便不同学习阶段的Dalao们切换. LCT总结--应用篇戳这里 概念.性质简述 首先介绍一下链剖分的概念(感谢laofu的讲课) 链剖分,是指一类 ...