题目大意:有一个串(全部由小写字母组成),现在要把它分成两部分,如果分开后的部分是回文串就计算出来它的价值总和,如果不是回文的那么价值就是0,最多能得到的最大价值。
分析:首先的明白这个最大价值有可能是负数,比如下面:
-1 -1 -1.....
aaa
这样的情况不管怎么分,分出来的串都是回文串,所以得到的最大价值是 -3。
求回文串的算法使用的是Manacher算法,线性的复杂度。
代码如下:
================================================================================================================
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; const int MAXN = 1e6+7; const int MAXM = 1007; const int oo = 1e9+7; char str[MAXN]; int p[MAXN], val[27], sum[MAXN]; bool Left[MAXN], Right[MAXN]; /** str[] 先存原字符串,后存扩展后的字符串 p[] p[i] 表示以i为中心的回文串有多长(只记录一边的长度)、 sum[] sum[i]表示前i个字符的总价值和 Left[] Left[i] 表示前缀长度为 i 的串是否是回文串 Right[] Right[i] 表示后缀长度为 i 的串是否是回文串 **/ void Manacher(char str[], int N) { int i, id=0; for(i=2; i<N; i++) { if(p[id]+id > i) p[i] = min( p[id*2-i], p[id]+id-i); else p[i] = 1; while(str[ i+p[i] ] == str[ i-p[i] ]) p[i]++; if(p[id]+id < p[i]+i) id = i; if(p[i] == i) Left[p[i]-1] = true; if(p[i]+i-1 == N) Right[p[i]-1] = true; } } int main() { int T; scanf("%d", &T); while(T--) { int i; memset(Left, false, sizeof(Left)); memset(Right, false, sizeof(Right)); memset(p, false, sizeof(p)); for(i=0; i<26; i++) scanf("%d", &val[i]); scanf("%s", str); int len = strlen(str); for(i=1; i<=len; i++) sum[i] = sum[i-1]+val[str[i-1]-'a']; for(i=len; i>=0; i--) { str[i+i+2] = str[i]; str[i+i+1] = '#'; } str[0] = '$'; Manacher(str, len+len+1); int ans = -oo; for(i=1; i<len; i++) { int temp = 0; if(Left[i] == true) temp += sum[i]; if(Right[len-i] == true) temp += sum[len]-sum[i]; ans = max(ans, temp); } printf("%d ", ans); } return 0; }