关于$\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. JavaScript Boolean(布尔) 对象

    创建 Boolean 对象 Boolean 对象代表两个值:"true" 或者 "false" 下面的代码定义了一个名为 myBoolean 的布尔对象: va ...

  2. X-Plane数据交互

    要用X-Plane进行二次开发,免不了需要进行参数的传递,下面我们来看看与X-Plane进行数据交互都有哪些方式. 与FSX和Flightgear基本一样,X-Plane支持插件,自然也支持通过插件进 ...

  3. mysql锁死的现象判断

    一般发生表锁死这种低级问题,就有两种情况:1.程序员水平太菜,2.程序逻辑错误. 一旦发生系统会出现超时,关键是有可能你看不到正在活动的php进程,而系统的慢查询日志也不会记录,只能通过show fu ...

  4. php中判断变量是否为空

    从数据库中取出值后判断是否为空,这个看起来很简单,只要和null比较一下就可以了,其实不然, if($obj==null){ } 这样写会报错的:Notice: Trying to get prope ...

  5. 关于SQL server的一些知识点

    关于怎么打开xp_cmdshell的方法: exec sp_configure 'show advanced option',1reconfiguregoexec sp_configure 'xp_c ...

  6. Spark的RDD编程(二)公众号undefined110

    创建RDD有两种方式:①读取外部数据集,lines=sc.textFile("README.md").②对一个集合进行并行化,lines=sc.parallelize([" ...

  7. winform布局格式

    一.默认布局 ★可以加panel,也可以不加: ★通过鼠标拖动控件的方式,根据自己的想法布局.拖动控件的过程中,会有对齐的线,方便操作: ★也可选中要布局的控件,在工具栏中有对齐工具可供选择,也有调整 ...

  8. 董的博客 hadoop

    董的博客 https://issues.apache.org/jira/browse/MAPREDUCE 很重要,把MAPREDUCE改为YARN即可 直接下载patch即可 http://horto ...

  9. 初始化rails上的compass项目

    compass以外还有一个很实用的scss模块, _media-queries.scss 通过终端下载 curl -O https://raw.github.com/paranoida/sass-me ...

  10. cf D Bear and Floodlight

    题意:有n个灯,每个灯有一个照亮的角度,现在从点(l,0)走到点(r,0),问这个人若一直被灯照着能最多走多远? 思路:状压dp,然后通过向量旋转求出点(dp[i[,0)与灯的坐标(p[j].x,p[ ...