Best Reward
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=3613
解题思路:
题目大意:
字母表的26个字母都有一个价值,给你一个字符串,将该字符串切成两份,对于每一份,如果是回文串,就获得该子串的字母价值
之和,否则该子串的价值为0。求出将字符串切成两份后能够获得的最大价值。
算法思想:先用Manacher算法求出以每个字母为中心的回文串的长度,并计算该字符串的前缀价值和。然后枚举切割点,得到两份
子串。这样就可以知道每个子串的中心点,然后检查以该子串的中心点作为中心点的回文串的长度,如果长度等于该子串的长度,
那么就加上该子串的价值。然后和最优价值比较就行了。
AC代码:
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int N = 500005;
char str[N*2];
int p[N*2],sum[N],val[27];//sum为前i个字符价值和
int pre[N],pos[N];//pre标记前i个字符为回文串,pos标记后i个字符为回文串
int main(){
int T;
scanf("%d",&T);
while(T--){
memset(pre,0,sizeof(pre));
memset(pos,0,sizeof(pos));
for(int i = 0; i < 26; ++i)
scanf("%d",&val[i]);
scanf("%s",str);
int len = strlen(str),id = 0,mx = 0;
int ans = -INF,tmp = 0;
for(int i = 1; i <= len; ++i)
sum[i] = sum[i-1]+val[str[i-1]-'a'];
for(int i = len; i >= 0; --i){
str[i+i+2] = str[i];
str[i+i+1] = '#';
}
str[0] = '*';str[2*len+2] = '\0';
for(int i = 2; str[i]; ++i){
p[i] = mx > i ? min(p[2*id-i], mx-i) : 1;
while(str[i + p[i]] == str[i - p[i]])
p[i]++;
if(i + p[i] > mx){
mx = i + p[i];
id = i;
}
if(i-p[i] == 0)
pre[p[i]-1] = 1;//表示前缀(前p[i]-1个字符)是回文串
if(i+p[i] == 2*len+2)
pos[p[i]-1] = 1;//表示后缀(后p[i]-1个字符)是回文串
}
for(int i = 1; i < len; ++i){
if(pre[i])
tmp += sum[i];
if(pos[len-i])
tmp += sum[len]-sum[i];
if(tmp > ans)
ans = tmp;
tmp = 0;
}
printf("%d\n",ans);
}
return 0;
}