标签:
关于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 }
标签:
原文地址:http://www.cnblogs.com/showson/p/4301627.html