HDU 3613(manacher)
题目链接:
题意:给出26个数字,代表a-z字母对应的价值,再给出一个字符串,求其切成两半总价值最大为多少,如果不是回文则价值为0.
题解:利用manacher算法,求的前缀为回文或后缀为回文的每一个位置,如果一个点之前的前缀为回文,则会有p[i]-i==0 (此时p[i]-1为前缀的长度,p[i]-1也是原字符串中的该字符的位置,比如第一个字符为a,则长度为1,位置也是第一个) 如果一个点之后的后缀为回文,则有i+p[i]==len+len+2(此时p[i]-1为后缀的长度,但是i不是)。
代码如下:

1 #include<iostream> 2 #include<stdio.h> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 const int maxn = 500000+50; 7 char s[maxn<<1]; 8 int p[maxn<<1]; 9 int sum[maxn],pre[maxn<<1],suf[maxn<<1]; 10 void manacher() //manacher 函数 11 { 12 int len=strlen(s); 13 for(int i=len;i>=0;--i) //将s扩大,中间加#,开头加* 14 { 15 s[i+i+2]=s[i]; 16 s[i+i+1]='#'; 17 } 18 s[0]='*'; 19 int id,mx=0; //mx代表以id为中心时,到达最远的位置 20 for(int i=1;i<len+len+1;++i) 21 { 22 if(mx>i) p[i]=min(p[2*id-i],mx-i); //如果到达最远位置大于当前匹配的地方,则p[i]取id对称点p和到达最远距离-i的小者 23 else p[i]=1; //如果i在mx右方,则p[i]=-1; 24 while(s[i-p[i]] == s[i+p[i]])++p[i]; //判断i回文长度 25 if(i+p[i]>mx) //看是否要更新最远距离,如果要,将此点作为中心。 26 { 27 id=i; 28 mx=p[i]+i; 29 } 30 } 31 } 32 33 int main() 34 { 35 int test; 36 int val[30]; 37 cin>>test; 38 while(test--) 39 { 40 memset(sum,0,sizeof(sum)); 41 memset(s,0,sizeof(s)); 42 memset(p,0,sizeof(p)); 43 memset(pre,0,sizeof(pre)); 44 memset(suf,0,sizeof(suf)); 45 for(int i=0;i<26;i++) 46 scanf("%d",&val[i]); 47 scanf("%s",s); 48 int len = strlen(s); 49 for(int i=1;i<=len;i++) //求前i个字符的总价值 50 sum[i]=sum[i-1]+val[s[i-1]-'a']; 51 manacher(); 52 for(int i=1;i<=len+len;i++) 53 { 54 if(p[i]-i==0) pre[p[i]-1]=-1; //如果pi-i长度的前缀是回文,标记 55 if(i+p[i]==len+len+2) suf[p[i]-1]=-2;//如果pi-i长度的后缀是回文,标记 56 /*此时需要注意一点,i为现字符的位置,p[i]-1为原字符的位置,在这个里面,pre记录的0下标是*#中#的,pre1才是有效,而在suf中,记录的是长度为多少的后缀*/ 57 } 58 int S=0,ans=0; 59 for(int i=1;i<len;i++){ 60 if(pre[i]==-1)S+=sum[i]; //如果该处是前缀回文,则S加上前缀的价值 61 if(suf[len-i]==-2) S+=sum[len]-sum[i]; //如果该处的后缀也是回文,则S为和,否则只计算前缀 62 if(S>ans) ans=S; //如果为当前最大,更新 63 S=0; //继续寻找 64 } 65 cout<<ans<<endl; 66 } 67 }