codeforces7D Palindrome Degree(manacher&dp或Hsh&dp)
题意:
定义回文串的度为length 即前半部分串的度或后半部分的度+1。先再给你一个字符串。
要你求出他全部前缀度的和。
思路:
先用manacher求出以每一个位置为中心最大回文串的长度。
dp[i]记录长度为i的前缀的度。那么dp[i+1]分奇偶用到前面的度即可了。
具体见代码:
#include<iostream> #include<string.h> #include<stdio.h> using namespace std; const int INF=0x3f3f3f3f; const int maxn=5110000; int p[maxn<<1],dp[maxn],len; char buf[maxn],st[maxn<<1]; void init() { int i; len=strlen(buf); st[0]='$',st[1]='#'; for(i=0;i<len;i++) st[2*i+2]=buf[i],st[2*i+3]='#'; len=2*len+2; } void manacher() { int i,id,mx=0; for(i=1;i<len;i++) { p[i]=mx>i?min(mx-i,p[2*id-i]):1; while(st[i+p[i]]==st[i-p[i]]) p[i]++; if(i+p[i]>mx) mx=i+p[i],id=i; } } int main() { int i,ans,n; while(~scanf("%s",buf)) { ans=0,n=strlen(buf); init(); manacher(); ans=dp[0]=1; for(i=1;i<n;i++) { if(p[i+2]-1>=i+1)//推断该前缀是否回文。字符串从0開始。
i+2为0到i在p数组对称中心无论回文串长度使是奇数还是偶数的。p[i]-1为最大回文长度 { if(i&1) dp[i]=dp[i/2]+1; else dp[i]=dp[i/2-1]+1; } ans+=dp[i]; } printf("%d\n",ans); } return 0; }
Hash的做法就比較简单了。
具体见代码:
#include<iostream> #include<string.h> #include<stdio.h> using namespace std; const int INF=0x3f3f3f3f; const int maxn=5110000;; unsigned long long H1[maxn],H2[maxn],xp[maxn],ha,hb,x=123; int dp[maxn],len; char buf[maxn],rbuf[maxn]; void init() { int i; len=strlen(buf); H1[len]=H2[len]=0; xp[0]=1; for(i=len-1;i>=0;i--) { H1[i]=H1[i+1]*x+buf[i]; H2[i]=H2[i+1]*x+rbuf[i]; xp[len-i]=xp[len-i-1]*x; } } unsigned long long getHash(unsigned long long *HS,int s,int L) { return HS[s]-HS[s+L]*xp[L]; } int main() { int i,ans; while(~scanf("%s",buf)) { ans=0,len=strlen(buf); for(i=0;i<len;i++) rbuf[i]=buf[len-i-1]; init(); ans=dp[0]=1; for(i=1;i<len;i++) { ha=getHash(H1,0,i+1); hb=getHash(H2,len-i-1,i+1); if(ha==hb) { if(i&1) dp[i]=dp[i/2]+1; else dp[i]=dp[i/2-1]+1; } else dp[i]=0; ans+=dp[i]; } printf("%d\n",ans); } return 0; }