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 }
View Code

 

posted on 2015-07-19 22:06  小松song  阅读(248)  评论(0)    收藏  举报

导航