题目链接:点击打开链接
题意:26个小写英文字母都有一个价值,给你一个字符串,将该字符串切成左右两半,对于每一半,如果是回文串,就获得该子串的字母价值之和,否则该子串的价值为0。求出将字符串切成两半后能够获得的最大价值。
思路一:KMP+思维。
思路二:manacher算法(马拉车算法)
具体思路明天写......
咳咳,回来写思路了......
主要介绍KMP+思维实现,基于manacher算法的方法和代码略。
思路:
首先要熟悉回文串的基本特征,正反读,字符串是一样的!!!题目说左右两半都是回文串,那么可以考虑把字符串前缀和后缀里所有的回文串找到,怎么找呢?先拿前缀考虑,如果把串反转,那么前缀的回文序列,在反转串的后缀里一定出现,利用KMP算法匹配原串前缀和反转串后缀,一定会匹配出前缀的最长回文序列,同理,匹配反转串前缀和原串后缀,可以匹配出原串的最长回文后缀。得到的是最长的回文串,可是我们需要标记出所有的呀,咋办?
思维处理出现了,还是拿前缀为例,已经匹配出的最长回文前缀设为s,如果s里面还存在一个最长的回文前缀t,t的长度肯定是小于s,而且,由于s回文,那么从s末尾向前读,读t的长度个字符,依次读取到的序列和t一定相同,这段序列记为tt,由于t回文,t和tt相同,那么tt是回文串,所以t和tt匹配,由此可以联想到Next函数,那么Next[s的长度数值]会是t的长度数值吗?可以利用反证法,假设利用Next函数得到的长度大于此长度,序列记做u,即u不是回文串,与此匹配的后缀v肯定是与u相同,由于s是回文串,v还要反向和u一致,所以v是回文串,与u不是回文串矛盾,
所以由s的Next得到的位置即为下一个最长回文前缀的位置,以此类推,得到所有的回文前缀。后缀思路一致,分析略。
分析到这,剩下的就很简单了,枚举分割点,计算左右价值总和,更新最大值。
// HDU 3613 Best Reward 运行/限制:93ms/1000ms
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
char s[500005],p[500005];//原串 反向串
int Next[500005];
int v[26],sum[500005];//字母对应的权值 原串权值前缀和
int pre[500005], pos[500005];//记录前缀为回文串的情况 记录后缀为回文串的情况(位置标号为反向串下标)
void getNext(char s[], int n) {
int i, j;
i = -1, j = 0;
Next[0] = -1;
while (j < n) {
if (i == -1 || s[i] == s[j]) {
i++; j++;
Next[j] = i;//不优化
}
else {
i = Next[i];
}
}
}
int kmp(char s[], char p[], int n) {
int i, j;
i = j = 0;
getNext(s, n);
while (j < n) {
if (i == -1 || p[j] == s[i]) {
i++; j++;
}
else {
i = Next[i];
}
}
return i;
}
int main(){
int t, len, k, re, ans;
scanf("%d", &t);
while (t--) {
re = -INF;
for (int i = 0; i < 26; i++) {
scanf("%d", &v[i]);
}
scanf("%s", s);
len = strlen(s);
memset(pre, 0, sizeof(pre));
memset(pos, 0, sizeof(pos));
p[0] = s[len - 1];
sum[0] = v[s[0] - 'a'];
for (int i = 1; i < len; i++) {
p[i] = s[len - i - 1];
sum[i] = sum[i - 1] + v[s[i] - 'a'];
}
p[len] = '\0';
k = kmp(s, p, len);
while (k > 0) {//记录前缀回文串
pre[k - 1] = 1;
k = Next[k];
}
k = kmp(p, s, len);
while (k > 0) {//记录后缀回文串
pos[k - 1] = 1;
k = Next[k];
}
for (int i = 0; i < len - 1; i++) {//枚举前半部分终点
ans = 0;
if (pre[i]) ans += sum[i];
if (pos[len - 2 - i]) ans += sum[len - 1] - sum[i];
re = max(re, ans);
}
printf("%d\n", re);
}
return 0;
}