题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=3613
题目意思:
a-z每个字母有一个值,给个字符串,让你分成两部分,每一部分至少有一个字符。
如果该串是回文字符串,则该值为所有字符的值之和,如果不是字符串,则为0.
让你计算怎样分割使得两部分的总和值最大。
解题思路:
利用回文字符串的next[i]也是回文字符串的特点,求出串中所有的回文串。
然后倒过来,求出后缀的会问字符串。
然后一次遍历就可以求出最大的值。
代码:
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<stack>
#include<list>
#include<queue>
#define eps 1e-6
#define INF (1<<30)
#define PI acos(-1.0)
using namespace std;
#define Maxn 510000
int value[27],sum[2][Maxn]; //分别表示从前至后的回文串的值和从后到前的回文串的值
int ss[2][Maxn],next[Maxn*2]; //从前之后求出各值
char save[Maxn];
char stemp[Maxn*2];
void getnext(char * a,int len)
{
int j=0;
next[1]=0;
for(int i=2;i<=len;i++)
{
while(j>0&&a[j+1]-a[i])
j=next[j];
if(a[j+1]==a[i])
j++;
next[i]=j;
}
return ;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
for(int i=1;i<=26;i++)
scanf("%d",&value[i]);
scanf("%s",save+1);
int len=strlen(save+1);
memset(ss,0,sizeof(ss));
for(int i=1;i<=len;i++) //预处理出所有的前缀值的和
ss[0][i]=ss[0][i-1]+value[save[i]-'a'+1];
for(int i=len;i>=1;i--) //预处理出所有的后缀值的和
ss[1][len-i+1]=ss[1][len-i]+value[save[i]-'a'+1];
strcpy(stemp+1,save+1);
stemp[len+1]='*';
strcpy(stemp+len+2,save+1);
// printf("%s\n",stemp+1);
std::reverse(stemp+len+2,stemp+len+len+2); //翻转
//printf("%s\n",stemp+1);
getnext(stemp,len+len+1);
memset(sum,0,sizeof(sum));
int i=len+len+1;
do //是回文串,注意只有一个元素也是回文串
{
i=next[i];
// printf("i:%d next[i]:%d\n",i,next[i]);
sum[0][i]=ss[0][i];
}while(next[i]);
//倒过来再求一遍
//printf("sum[0][1]:%d\n",sum[0][1]);
std::reverse(stemp+1,stemp+len+1);
std::reverse(stemp+len+2,stemp+len+len+2);
getnext(stemp,len+len+1);
i=len+len+1;
do
{
i=next[i];
sum[1][i]=ss[1][i];
}while(next[i]);
int ans=-INF;
for(int i=1;i<len;i++)
ans=max(ans,sum[0][i]+sum[1][len-i]);
printf("%d\n",ans);
}
return 0;
}