关于$\mathrm{Manacher}$算法,网上介绍已经很全面 这里说一下自己的理解

这里的$rad$数组:$rad_i$表示以以位置i为中心的最长回文串的回文半径(不包括i这个点)。

朴素的思想大概是从每个点出发像两边扩展,大概$O(n^2)$复杂度?据说$\mathrm{Manacher}$是$O(n)$的(不会证,Orz,大概因为每个位置只会被暴力扩展$O(1)$次)这是因为回文串有对称性,我们可以利用这点来优化算法。现在假设我们已经得到了$i$和$i$以前的$rad$值,现在想直接通过$O(1)$的时间计算出i右边一些点的$rad$值。设$k$从$1$到$rad_i$,表示现在想直接计算出$rad_{i+k}$的$rad$值。则有下列情况

其中

红色:$rad_i$
橙色:$rad_{i-k}$
绿色:$rad_{i-k}$

①$rad_i-k<rad_{i-k}$————————————————————————————————————————————————————————————

此时$rad_{i+k}$一定为$rad_i-k$否则根据对称性,$rad_i$可以更大。

②$rad_i-k>rad_{i-k}$————————————————————————————————————————————————————————————

此时根据对称性也可以很显然地看出$rad_{i+k}=rad_{i-k}$

由①②有,当$rad_i-k\not=rad_{i-k}$时,$rad_{i+k}=\min{\{rad_{i-k},rad_i-k\}}$

那么$rad_i-k=rad_{i+k}$时怎么办呢

③$rad_i-k=rad_{i-k}$————————————————————————————————————————————————————————————

这时即使$rad_{i+k}>rad_{i-k}$也没有矛盾,此时应当令i+=k用朴素的算法扩大$rad_i$之后再用这个$rad_i$迭代更新。

代码:

for(int i=1,j=0,k;i<=len;){
for(;s[i-j-1]==s[i+j+1];j++);
rad[i]=j;
for(k=1;k<=j && rad[i]-k!=rad[i-k];k++)
rad[i+k]=min(rad[i]-k,rad[i-k]);
i+=k;
j=max(j-k,0);
}

但是这样只能求出长度为奇数的回文串的长度,对于偶数,我们这样处理。

char s[Maxn]={0};
s[0]='*';
for(int i=0;i<_len;i++){
s[++len]=_s[i];
s[++len]='#';
}
s[len]='&';

之后再按上面的方法求即可。

然后这里再说一下$\mathrm{SCOI2013}$的密码,用了$\mathrm{Manacher}$的思想。(题目链接http://acm.uestc.edu.cn/#/problem/show/128

很容易想到朴素的算法,把必须为相同字符的合并为一个集合(用并查集实现),然后对必须不相同的集合连边,从集合向集合中的元素连边。后一步是$O(n)$的,而前一步最坏是$O(n^2)$对于$10^5$的数据显然无法承受,这里很自然想到$\mathrm{Manacher}$的$O(n)$

$i$从$1$开始,维护$rad_i+i$的最大值,为$MX_r$,这样的$i$记为$MX_{id}$,然后显然我们只需要从$i+\max{\{0, \min{\{MX_r-i,rad_{2MX_{id}-i}\}}\}}$开始合并,大概又是$O(n)$的

完整代码

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#define dout printf
using namespace std; const int Maxn=100000+10;
int n,rad[Maxn*2];
int col[Maxn*2],cannot[Maxn*2][30],cnt=0;
int stk[30],top;
bool instk[Maxn*2];
inline void getint(int&x){
char c=getchar();
for(x=0;!isdigit(c);c=getchar());
for(;isdigit(c);c=getchar())x=x*10+c-'0';
}
struct Edge{int b;Edge*next;}edges[Maxn*3*2],*firc[Maxn*2],*fird[Maxn*2];int tot;
void AddEdge(int a,int b,Edge*fir[]){
edges[++tot]=(Edge){b,fir[a]};fir[a]=edges+tot;
}
int fa[Maxn*2];
int Find(const int&x){
return fa[x]==x?x:fa[x]=Find(fa[x]);
}
bool Union(int x,int y){
x=Find(x),y=Find(y);
if(x==y)return 0;
return fa[y]=x,1;
}
void input(){
getint(n);
for(int i=1;i<=n;i++)getint(rad[(i<<1)-1]);
for(int i=1;i<n;i++)getint(rad[i<<1]);
} void work(const int n2=n*2){
int MX_r=1,MX_id=1;
char*ans=new char[Maxn];
memset(ans,0,sizeof(*ans)*Maxn);
for(int i=1;i<=n2;i++)fa[i]=i;
for(int i=2;i<=n2;i++){
for(int j=max(0, min(MX_r-i,rad[MX_id*2-i]) );i-j>0&&i+j<=n2&&j<=rad[i];j++) {
Union(i-j,i+j);
}
if(rad[i]+i>MX_r)MX_r=i+rad[i],MX_id=i;
}
for(int f,i=1;f=Find(i),i<=n2;i+=2)
AddEdge(f,(i+1)>>1,firc);
for(int f1,f2,d,i=2;i<=n2;i++){
d=rad[i]+1;
f1=Find(i-d),f2=Find(i+d);
AddEdge(f1,f2,fird);
AddEdge(f2,f1,fird);
}
for(int x,real,f,i=1;real=(i+1)>>1,i<=n2;i+=2)if(!ans[real]){
x=1;f=Find(i);
for(;cannot[f][x];x++);
for(Edge*p=fird[f];p;p=p->next)cannot[p->b][x]=1;
for(Edge*p=firc[f];p;p=p->next)ans[p->b]=x+'a'-1;
}
puts(ans+1);
delete ans;
}
int main(){
freopen("password.in","r",stdin);
freopen("password.out","w",stdout); input();
work(); return 0;
}

Manacher思想 SCOI2013 密码的更多相关文章

  1. 【BZOJ3325】[Scoi2013]密码 Manacher

    [BZOJ3325][Scoi2013]密码 Description Fish是一条生活在海里的鱼.有一天他很无聊,就到处去寻宝.他找到了位于海底深处的宫殿,但是一扇带有密码锁的大门却阻止了他的前进. ...

  2. 【bzoj3325】[Scoi2013]密码 逆模拟Manacher

    题目描述 给出一个只包含小写字母的字符串的长度.以每一个字符为中心的最长回文串长度.以及以每两个相邻字符的间隙为中心的最长回文串长度,求满足条件的字典序最小的字符串. 输入 输入由三行组成.第一行仅含 ...

  3. BZOJ3325 [Scoi2013]密码 【manacher】

    题目 Fish是一条生活在海里的鱼.有一天他很无聊,就到处去寻宝.他找到了位于海底深处的宫殿,但是一扇带有密码锁的大门却阻止了他的前进.通过翻阅古籍,Fish 得知了这个密码的相关信息: 该密码的长度 ...

  4. BZOJ 3325 [SCOI2013]密码 (逆模拟Manacher+构造)

    题目大意:给你一个字符串每个位置和相邻两个位置为回文中心的最长回文串长度,让你构造一个合法的字典序最小的字符串 挺有意思的构造题 首先按照$Manacher$的思想还原$p$数组 定义$f_{ij}$ ...

  5. BZOJ3325 [Scoi2013]密码【Manacher】【构造】【贪心】

    Description Fish是一条生活在海里的鱼.有一天他很无聊,就到处去寻宝.他找到了位于海底深处的宫殿,但是一扇带有密码锁的大门却阻止了他的前进.通过翻阅古籍,Fish 得知了这个密码的相关信 ...

  6. 2019.03.28 bzoj3325: [Scoi2013]密码(manacher+模拟)

    传送门 题意: 现在有一个nnn个小写字母组成的字符串sss. 然后给你nnn个数aia_iai​,aia_iai​表示以sis_isi​为中心的最长回文串串长. 再给你n−1n-1n−1个数bib_ ...

  7. SCOI2013 密码

    题目描述: Fish是一条生活在海里的鱼.有一天他很无聊,就到处去寻宝.他找到了位于海底深处的宫殿,但是一扇带有密码锁的大门却阻止了他的前进. 通过翻阅古籍,Fish 得知了这个密码的相关信息: 该密 ...

  8. luogu P3279 [SCOI2013]密码

    LINK:密码 给出来manacher的数组 让还原出字典序最小的字符串.字符集为小写字母. 当没有任何限制时 放字典序最小的'a'.如果此时还在最长的回文串中的话那么 直接得到当前字符即可. 注意这 ...

  9. BZOJ3325 : [Scoi2013]密码

    从以每一位为中心的回文串长度可以用Manacher倒推出$O(n)$对相等和不等关系. 将相等的用并查集维护,不等的连边. 然后输出方案时若还没被染过色,则求一个mex. #include<cs ...

随机推荐

  1. node 裁剪图片

    1.前端一般用Jcrop这个jq插件 要返回 x: 图片 x坐标 y: 图片 y坐标 w: 图片 宽度 h: 图片 高度 2.node 实现 var images = require("im ...

  2. java_设计模式_模板方法模式_Template Method Pattern(2016-08-11)

    定义: 定义一个操作中算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变算法的结构即可重定义该算法中的某些特定步骤.这里的算法的结构,可以理解为你根据需求设计出来的业务流程.特定的步骤就是指那些 ...

  3. windows编程中 一些前缀区分 IDR和IDD

    IDC_:控件的ID命名前缀(Control) IDM_:菜单的ID命名前缀(Menu) IDD_:对话框的ID命名前缀(Dialog) IDR_:资源的ID命名前缀(Resource) IDS_:字 ...

  4. Oracle数据库之动态SQL

    Oracle数据库之动态SQL 1. 静态SQLSQL与动态SQL Oracle编译PL/SQL程序块分为两个种:一种为前期联编(early binding),即SQL语句在程序编译期间就已经确定,大 ...

  5. 基于jQuery 的图片瀑布流实现

    解题思路: 第1步  分析问题:我这边的处理方式是以列为单位.每次滚动条滚到底部,把需要加的新的内容放在高度最小的列.如下图所示 加载后的显示 如果在继续往下滚动.新图片就会在1下边显示,如此类推. ...

  6. 在静态页面html中跳转传值

    在html中通过"?"传值--------<a href="index2.html?name=caoy">静态传值</a> 在跳转到的页 ...

  7. 入门5:PHP 语法基础——流程控制

    一.if...else 语句 if( ) else{ } 如果 .... 就.... 否则.... if(判断){ 判断成立 则执行该表达式 }else{ 如果上方判断都不成立 则执行该表达式 } i ...

  8. 一次性安装src.rpm编译所依赖的软件包

    yum-builddep SRPMS/fcitx-4.2.8.4-4.1.cgdl21.src.rpm NAME       yum-builddep - install missing depend ...

  9. C语言学习之笔记

    第一章 概述 1. C语言的特点 ①语言简洁.紧凑,使用方便.灵活.共有32个关键字(也称保留字),9种控制语句. ②运算符丰富,共有34种运算符. ③数据结构丰富,数据类型有:整型.实型.字符型.数 ...

  10. Java中异常处理和设计

    在程序设计中,进行异常处理是非常关键和重要的一部分.一个程序的异常处理框架的好坏直接影响到整个项目的代码质量以及后期维护成本和难度.试想一下,如果一个项目从头到尾没有考虑过异常处理,当程序出错从哪里寻 ...