Description
给出一个只由小写字母组成的字符串以及每个小写字母的价值,现要将这个字符串分成两半,如果某一半是回文串则将累加这一半串的价值(价值即为这个串中每个字符的价值之和),问能得到的最大价值
Input
第一行为一整数T表示用例组数,每组用例首先输入26个整数表示a到z这26个小写字母的价值,之后输入长度不超过500000的一个字符串
Output
对于每组用例,输出将该字符串分成两半后能得到的最大价值
Sample Input
2
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
aba
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
acacac
Sample Output
1
6
Solution
问题在于如果快速判断a串的前缀和后缀是否是回文串,这里只说前缀(因为后缀就是反串的前缀),考虑串a与其反串b的匹配,如果a的某个前缀是回文串,那么因为其反串是b串的一个后缀,所以只要extend[i]=len-i,那么前缀a[0~i]就是一个回文串,同理,在b与a的匹配中,如果extend[i]=len-i,那么后缀a[len-1-i,len-1]就是一个回文串,做两边扩展kmp通过extend数组可以得到以第i个字符结尾或者开始的前后缀是否为回文串,之后O(n)枚举切割点更新最大价值即可(需要预处理出价值前缀和)
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 555555
char a[maxn],b[maxn];
int T,v[33],sum[maxn],flag1[maxn],flag2[maxn],nex[maxn],extend[maxn];
void extend_kmp(char *a,char *b)
{
int i,j,k,la=strlen(a),lb=strlen(b);
for(i=0;i+1<la&&a[i+1]==a[i];i++);
nex[1]=i;
k=1;
for(i=2;i<la;i++)
{
int len=k+nex[k];
nex[i]=min(nex[i-k],max(0,len-i));
while(i+nex[i]<la&&a[nex[i]]==a[i+nex[i]])nex[i]++;
if(i+nex[i]>k+nex[k])k=i;
}
for(i=0;i<la&&i<lb&&a[i]==b[i];i++);
extend[0]=i;
k=0;
for(i=1;i<lb;i++)
{
int len=k+extend[k];
extend[i]=min(nex[i-k],max(0,len-i));
while(i+extend[i]<la&&i+extend[i]<lb&&a[extend[i]]==b[extend[i]+i])
extend[i]++;
if(k+extend[k]<i+extend[i])k=i;
}
}
int main()
{
scanf("%d",&T);
while(T--)
{
memset(flag1,0,sizeof(flag1));
memset(flag2,0,sizeof(flag2));
for(int i=0;i<26;i++)scanf("%d",&v[i]);
scanf("%s",a);
int len=strlen(a);
for(int i=0;i<len;i++)b[i]=a[len-1-i];
sum[0]=v[a[0]-'a'];
for(int i=0;i<len;i++)sum[i]=sum[i-1]+v[a[i]-'a'];
extend_kmp(a,b);
for(int i=0;i<len;i++)if(extend[i]==len-i)flag1[len-1-i]=1;
extend_kmp(b,a);
for(int i=0;i<len;i++)if(extend[i]==len-i)flag2[i]=1;
int ans=v[a[0]-'a'];
for(int i=0;i<len-1;i++)
{
int temp=flag1[i]*sum[i]+flag2[i+1]*(sum[len-1]-sum[i]);
ans=max(ans,temp);
}
printf("%d\n",ans);
}
return 0;
}