题意:
给你26个字母的价值 和 一个字符串,将字符串分成两份,是回文串的那一份的价值是这份串所有字母价值的和,不是回文串的那份价值为0,输出最大价值
思路:
用manacher算法求出回文串的长度 和 枚举切割每一个点,检查得到的两个子串是否是回文串,再求出字符串的前缀和。
最后遍历一遍,将每一个切割点所对应的价值求出,输出最大值
输入的字符串只有一个字符时价值为0
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=5e5+10;
int len[2*maxn],v[50];
int sum[maxn]; //sum[i]为前i个字符的前缀和
bool pre[maxn],lat[maxn]; //pre[i]表示前i个字符是回文串
char s[maxn],str[2*maxn]; //lat[i]表示后i个字符是回文串
int id,mx;
void Manacher( )
{
memset(str,0,sizeof(str));
memset(len,0,sizeof(len));
memset(pre,false,sizeof(pre));
memset(lat,false,sizeof(lat));
int k=0;
str[k++] = '$';
int slen = strlen(s);
for(int i=0;i<slen;i++)
{
str[k++] = '#';
str[k++] = s[i];
}
str[k++] = '#';
id = mx = 0;
for(int i=1;i<k;i++)
{
if(i < mx)
len[i] = min(mx-i,len[2*id-i]);
else
len[i] = 1;
while(str[i-len[i]]==str[i+len[i]])
len[i]++;
if(len[i]+i > mx)
{
mx = len[i]+i;
id = i;
}
if(i-len[i]==0) //表示前缀(前len[i]-1个字符)是回文串
pre[len[i]-1] = 1;
if(i+len[i]==2*slen+2) //表示后缀(后len[i]-1个字符)是回文串
lat[len[i]-1] = 1;
}
}
int main()
{
int t,ans;
scanf("%d",&t);
while(t--)
{
ans = 0;
for(int i=0;i<26;i++)
scanf("%d",&v[i]);
scanf("%s",s);
int slen = strlen(s);
sum[1] = v[s[0]-'a']; //sun[1]表示第一个字符的前缀和,字符串的第一个字符为s[0]
for(int i=2;i<=slen;i++)
sum[i] = sum[i-1]+v[s[i-1]-'a']; //前缀和
Manacher();
for(int i=1;i<slen;i++)
{
int cnt = 0;
if(pre[i])
cnt += sum[i];
if(lat[slen-i])
cnt += sum[slen]-sum[i];
ans = max(ans,cnt);
}
printf("%d\n",ans);
}
return 0;
}