码迷,mamicode.com
首页 > 其他好文 > 详细

Manacher思想 SCOI2013 密码

时间:2015-02-26 18:17:57      阅读:191      评论:0      收藏:0      [点我收藏+]

标签:

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

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

朴素的思想大概是从每个点出发像两边扩展,大概O(n^2)复杂度?据说Manacher是O(n)的(不会证,Orz)这是因为回文串有对称性,我们可以利用这点来优化算法。现在假设我们已经得到了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!=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]迭代更新。

代码:

1 for(int i=1,j=0,k;i<=len;){
2     for(;s[i-j-1]==s[i+j+1];j++);
3     rad[i]=j;
4     for(k=1;k<=j && rad[i]-k!=rad[i-k];k++)
5         rad[i+k]=min(rad[i]-k,rad[i-k]);
6     i+=k;
7     j=max(j-k,0);
8 }

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

1 char s[Maxn]={0};
2 s[0]=*;
3 for(int i=0;i<_len;i++){
4     s[++len]=_s[i];
5     s[++len]=#;
6 }
7 s[len]=&;

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

 

 

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

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

i从1开始,维护rad[i]+i的最大值,为MX_r,这样的i记为MX_id,然后显然我们只需要从i+max(0, min(MX_r-i,rad[MX_id*2-i])开始合并,大概又是O(n)的

完整代码

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<cstdio>
 5 #include<algorithm>
 6 #define dout printf
 7 using namespace std;
 8 
 9 const int Maxn=100000+10;
10 int n,rad[Maxn*2];
11 int col[Maxn*2],cannot[Maxn*2][30],cnt=0;
12 int stk[30],top;
13 bool instk[Maxn*2];
14 inline void getint(int&x){
15     char c=getchar();
16     for(x=0;!isdigit(c);c=getchar());
17     for(;isdigit(c);c=getchar())x=x*10+c-0;
18 }
19 struct Edge{int b;Edge*next;}edges[Maxn*3*2],*firc[Maxn*2],*fird[Maxn*2];int tot;
20 void AddEdge(int a,int b,Edge*fir[]){
21     edges[++tot]=(Edge){b,fir[a]};fir[a]=edges+tot;
22 }
23 int fa[Maxn*2];
24 int Find(const int&x){
25     return fa[x]==x?x:fa[x]=Find(fa[x]);
26 }
27 bool Union(int x,int y){
28     x=Find(x),y=Find(y);
29     if(x==y)return 0;
30     return fa[y]=x,1;
31 }
32 void input(){
33     getint(n);
34     for(int i=1;i<=n;i++)getint(rad[(i<<1)-1]);
35     for(int i=1;i<n;i++)getint(rad[i<<1]);
36 }
37 
38 void work(const int n2=n*2){
39     int MX_r=1,MX_id=1;
40     char*ans=new char[Maxn];
41     memset(ans,0,sizeof(*ans)*Maxn);
42     for(int i=1;i<=n2;i++)fa[i]=i;
43     for(int i=2;i<=n2;i++){
44         for(int j=max(0, min(MX_r-i,rad[MX_id*2-i]) );i-j>0&&i+j<=n2&&j<=rad[i];j++) {
45             Union(i-j,i+j);
46         }
47         if(rad[i]+i>MX_r)MX_r=i+rad[i],MX_id=i;
48     }
49     for(int f,i=1;f=Find(i),i<=n2;i+=2)
50         AddEdge(f,(i+1)>>1,firc);
51     for(int f1,f2,d,i=2;i<=n2;i++){
52         d=rad[i]+1;
53         f1=Find(i-d),f2=Find(i+d);
54         AddEdge(f1,f2,fird);
55         AddEdge(f2,f1,fird);
56     }
57     for(int x,real,f,i=1;real=(i+1)>>1,i<=n2;i+=2)if(!ans[real]){
58         x=1;f=Find(i);
59         for(;cannot[f][x];x++);
60         for(Edge*p=fird[f];p;p=p->next)cannot[p->b][x]=1;
61         for(Edge*p=firc[f];p;p=p->next)ans[p->b]=x+a-1;
62     }
63     puts(ans+1);
64     delete ans;
65 }
66 int main(){
67     freopen("password.in","r",stdin);
68     freopen("password.out","w",stdout);
69     
70     input();
71     work();
72     
73     return 0;
74 }

 

Manacher思想 SCOI2013 密码

标签:

原文地址:http://www.cnblogs.com/showson/p/4301627.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!