题意:上面的数字是26个字母的价值,接下来26个英文字母组成的字符串,将它分成两份,如果分后的串是回文串,则这个串的价值是这个串的所有字母的和,如果不是回文串则这个串价值为0。求最大价值和。
看到回文串,第一时间可以想到manacher算法。只要得到了以第
i
位为中心的最长回文串长度的一半
我们可以枚举切割点
i
,容易求出左子串的中心
如果某一子串回文,我们就要计算它的价值和。这时候每一次都计算显然会很慢,我们可以用部分和
到这里问题就基本解决了,时间复杂度也只是线性的。
#include <algorithm>
#include <climits>
#include <cstdio>
#include <cstring>
using namespace std;
const int charset = 26;
const int maxlen = 5e5 + 9;
int value[charset + 5];
int presum[maxlen << 1];
char st[maxlen << 1], tmp[maxlen << 1];
int len;
int f[maxlen << 1];
void init() {
for(int i = 0; i < charset; i++) scanf("%d", &value[i]);
scanf("%s", tmp);
st[0] = '!';
len = 1;
int tlen = strlen(tmp);
for(int i = 0; i < tlen; i++) {
st[len++] = '#';
st[len++] = tmp[i];
}
st[len++] = '#';
st[len++] = '?';
}
void get_sum() {
presum[0] = presum[1] = 0;
presum[2] = value[st[2] - 'a'];
for(int i = 3; i < len; i++) {
presum[i] = presum[i - 1];
if(st[i] != '#') presum[i] += value[st[i] - 'a'];
}
}
void manacher() {
f[0] = f[1] = 0;
int p = 1;
for(int i = 2; i < len; i++) {
f[i] = max(0, min(f[(p << 1) - i], p + f[p] - i));
while(st[i - f[i] - 1] == st[i + f[i] + 1]) ++f[i];
if(i + f[i] > p + f[p]) p = i;
}
}
void solve() {
init();
get_sum();
manacher();
int ans = INT_MIN;
int left_mid = 2;
int right_mid = ((len - 4) >> 1) + 3;
for(int i = 3; i + 2 < len; i += 2) {
int s = 0;
if(left_mid + f[left_mid] == i) s += presum[i];
if(right_mid - f[right_mid] == i) s += presum[len - 3] - presum[i];
++left_mid;
++right_mid;
ans = max(ans, s);
}
printf("%d\n", ans);
}
int main() {
freopen("hdu3613.in", "r", stdin);
freopen("hdu3613.out", "w", stdout);
int caset;
scanf("%d", &caset);
for (int casei = 0; casei < caset; casei++) solve();
return 0;
}