Best Reward HDU - 3613
题目链接:https://cn.vjudge.net/contest/163024#problem/S
题目大意:26个字母,每个都有一个权值,给定一字符串,将这个字符串分给成两部分,分割后的两部分,如果该部分是回文的话它的价值就是所有字母的权值和,否则就是0。
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
output
1
6
题目分析:可以预处理一下,这个串在第i点分隔开之后是否是回文,然后用前缀和处理一下权值即可。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
int p[maxn], v[30], sum[maxn], cnt, len;
bool Left[maxn], Right[maxn];
char str[maxn], s[maxn];
void init()
{
memset(p, 0, sizeof(p));
int i;
cnt = 0;
str[cnt++] = '$';
for(i = 0; i < len; i++)
{
str[cnt++] = '#';
str[cnt++] = s[i];
}
str[cnt++] = '#';
str[cnt] = '\0';
}
void manacher()
{
init();
int po = 0, maxr = 0;
for(int i = 1; i < cnt; i++)
{
if(maxr > i) p[i] = min(p[po * 2 - i], maxr - i);
else p[i] = 1;
while(str[i + p[i]] == str[i - p[i]]) p[i]++;
if(i + p[i] > maxr)
{
maxr = i + p[i];
po = i;
}
if(p[i] == i) Left[i - 1] = true;
if(p[i] + i == cnt) Right[p[i] - 1] = true;
}
}
int main()
{
int t, i;
scanf("%d", &t);
while(t--)
{
for(i = 0; i < 26; i++)
scanf("%d", &v[i]);
scanf("%s", s);
len = strlen(s);
memset(sum, 0, sizeof(sum));
memset(Left, false, sizeof(Left));
memset(Right, false, sizeof(Right));
sum[0] = v[s[0] - 'a'];
for(i = 1; i < len; i++)
{
sum[i] = sum[i - 1] + v[s[i] - 'a'];
}
manacher();
int ans = 0;
for(i = 1; i < len; i++)
{
int tmp = 0;
if(Left[i])
{
tmp += sum[i - 1];
}
if(Right[len - i])
{
tmp += sum[len - 1] - sum[i - 1];
}
ans = max(tmp, ans);
}
printf("%d\n", ans);
}
}
弄不懂的地方是:
if(p[i] == i) Left[i - 1] = true;
if(p[i] + i == cnt) Right[p[i] - 1] = true;
这两行代码。
举个例子:abaaba
处理之后的是这样的
位置:0 1 2 3 4 5 6 7 8 9 10 11 12 13
字符:$ # a # b # a # a # b # a #
p[i]: 1 2 1 4 1 2 7 2 1 4 1 2 1
p[4] = 4,这时left[3] = true; 它的意思是 前缀 aba是个回文串。因为p[4] = 4,说明在str[1~3]个字符与后边[5~7]个字符组成了回文(当然是加上了4个字符),因为这个字符串是加上了#的,所以p[4]=4,是说明一共有3 * 2 + 1个字符,对应回原来的就是i- 1;
right同理,只不过处理的是后缀。