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]双倍回文 (回文自动机)的更多相关文章

  1. bzoj千题计划305:bzoj2565: 最长双回文串(回文自动机)

    https://www.lydsy.com/JudgeOnline/problem.php?id=2565 正着构造回文自动机 倒过来再构造一个回文自动机 分别求出以位置i开始的和结尾的最长回文串 # ...

  2. bzoj千题计划300:bzoj4823: [Cqoi2017]老C的方块

    http://www.lydsy.com/JudgeOnline/problem.php?id=4823 讨厌的形状就是四联通图 且左右各连一个方块 那么破坏所有满足条件的四联通就好了 按上图方式染色 ...

  3. bzoj千题计划196:bzoj4826: [Hnoi2017]影魔

    http://www.lydsy.com/JudgeOnline/problem.php?id=4826 吐槽一下bzoj这道题的排版是真丑... 我还是粘洛谷的题面吧... 提供p1的攻击力:i,j ...

  4. bzoj千题计划280:bzoj4592: [Shoi2015]脑洞治疗仪

    http://www.lydsy.com/JudgeOnline/problem.php?id=4592 注意操作1 先挖再补,就是补的范围可以包含挖的范围 SHOI2015 的题 略水啊(逃) #i ...

  5. bzoj千题计划177:bzoj1858: [Scoi2010]序列操作

    http://www.lydsy.com/JudgeOnline/problem.php?id=1858 2018 自己写的第1题,一遍过 ^_^ 元旦快乐 #include<cstdio> ...

  6. bzoj千题计划317:bzoj4650: [Noi2016]优秀的拆分(后缀数组+差分)

    https://www.lydsy.com/JudgeOnline/problem.php?id=4650 如果能够预处理出 suf[i] 以i结尾的形式为AA的子串个数 pre[i] 以i开头的形式 ...

  7. bzoj千题计划304:bzoj3676: [Apio2014]回文串(回文自动机)

    https://www.lydsy.com/JudgeOnline/problem.php?id=3676 回文自动机模板题 4年前的APIO如今竟沦为模板,,,╮(╯▽╰)╭,唉 #include& ...

  8. bzoj千题计划292:bzoj2244: [SDOI2011]拦截导弹

    http://www.lydsy.com/JudgeOnline/problem.php?id=2244 每枚导弹成功拦截的概率 = 包含它的最长上升子序列个数/最长上升子序列总个数 pre_len ...

  9. bzoj千题计划278:bzoj4590: [Shoi2015]自动刷题机

    http://www.lydsy.com/JudgeOnline/problem.php?id=4590 二分 这么道水题 没long long WA了两发,没判-1WA了一发,二分写错WA了一发 最 ...

随机推荐

  1. Install alipay支付宝安全控件 on firefox in linux

    [root@rgqancy 下载]# ./aliedit.sh建议以非root账号安装支付宝安全控件请重启   firefox   使插件生效成功安装 支付宝安全控件请按任意键退出... what i ...

  2. 金蝶特性配置(超级BOM)

    特性配置 特性物料 物料和特性的对应关系 自动新增物料的系统设置 特性物料自动新增 自动新增的特性配置方案 相关表 ICPlan_CharacteristicEntry

  3. pandas文件写入读取操作

    #encoding:utf8 import pandas as pd import numpy as np from pylab import * df=pd.read_csv("./pat ...

  4. 设计模式笔记:适配器模式(Adapter)

    1. 适配器模式简介 1.1 模式定义 适配器模式:通过一个类的接口转换成客户希望的另外一个接口,使原本由于接口不兼容而不能一起工作的那些类可以一起工作. 适配器从结构上分为:类适配器和对象适配器.其 ...

  5. JavaScript实现表单的全选,反选,获取值

    构思 通过for循环和for in循环来实现,界面效果如下 步骤 全选: 循环给所有的表单设置checked 反选: 循环内判断checked是否为true,如果为true则改为false否则改为tr ...

  6. Maven依赖中的scope详解

    scope的分类 compile 默认就是compile,什么都不配置也就是意味着compile.compile表示被依赖项目需要参与当前项目的编译,当然后续的测试,运行周期也参与其中,是一个比较强的 ...

  7. MT【6】等比数列和的极限

    评:利用等比数列求和公式给出所求数列,再利用反证法证明唯一性.这种反证方法印象中本科高等代数里讲初等对称多项式时出现过.

  8. CAN通信详解

    30.1 CAN简介 30.2 硬件设计 30.3 软件设计 30.4 下载验证 CAN 是Controller Area Network 的缩写(以下称为CAN),是ISO国际标准化的串行通信协议. ...

  9. hdu 2577 How to Type(dp)

    Problem Description Pirates have finished developing the typing software. He called Cathy to test hi ...

  10. 在kubernetes集群上用helm安装Datadog(Monitoring)

    Datadog is a monitoring service that gathers monitoring data from your containers within your Azure ...