HDU 3613 Best Reward ( 拓展KMP求回文串 || Manacher )
题意 : 给个字符串S,要把S分成两段T1,T2,每个字母都有一个对应的价值,如果T1,T2是回文串,那么他们就会有一个价值,这个价值是这个串的所有字母价值之和,如果不是回文串,那么这串价值就为0。问最多能获得多少价值?
分析 : 有两种做法,第一种是拓展KMP正反跑两次或者Manacher
①如果我们用原串去和反转串( 即将原串反转 )跑一次拓展KMP得到的 extend 能够帮助我们得到原串前缀的某一段是否是回文串,仅当 extend[i] = 整个后缀长度,而用反转串去和原串跑一次拓展KMP得到的另一个 extend 能够帮助我们得到原串的某个后缀是否是回文串,借助这两个 extend 我们就能得到每一个字符作为分割点时候的价值了,当然提前处理原串的前缀价值和会快速更多
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
const int maxn = 5e5 + 10;
int Next[maxn], moL;
void GetNext(char * mo)
{
Next[0] = moL;
int a, p;
for (int i = 1, j = -1; i < moL; i++, j--){
if (j < 0 || i + Next[i - a] >= p){
if (j < 0) p = i, j = 0;
while (p < moL && mo[p] == mo[j]) p++, j++;
Next[i] = j;
a = i;
} else Next[i] = Next[i - a];
}
}
void GetExtend(char * S, char *mo, int * ext)
{
GetNext(mo);
int a, p;
int strL = moL;
for (int i = 0, j = -1; i < strL; i++, j--){
if (j < 0 || i + Next[i - a] >= p){
if (j < 0) p = i, j = 0;
while (p < strL && j < moL && S[p] == mo[j])
p++, j++;
ext[i] = j;
a = i;
} else ext[i] = Next[i - a];
}
}
int sum[maxn], val[27];
inline void GetSum(char * mo)
{
memset(sum, 0, sizeof(sum));
sum[0] = val[mo[0]-'a'];
for(int i=1; i<moL; i++){
sum[i] += sum[i-1] + val[mo[i]-'a'];
}
}
char mo[maxn], Rmo[maxn];
int extend[maxn], Rextend[maxn];
int main(void)
{
int nCase;
scanf("%d", &nCase);
while(nCase--){
for(int i=0; i<26; i++)
scanf("%d", &val[i]);
scanf("%s", mo);
memcpy(Rmo, mo, sizeof(mo));
moL = strlen(mo);
std::reverse(Rmo, Rmo+moL);
GetSum(mo);
GetExtend(Rmo, mo, extend);///用原串跑反转串
GetExtend(mo, Rmo, Rextend);///用反转串跑原串
int ans = 0;
for(int i=1; i<moL; i++){
int tmp = 0;
if(extend[i] == moL-i) tmp += sum[moL-i-1];///这里的关系可能有点烦,拿个例子推一推就行了
if(Rextend[moL-i] == i) tmp += sum[moL-1]-sum[moL-i-1];
ans = max(tmp, ans);
}
printf("%d\n", ans);
}
return 0;
}
②如果用Manacher的话就每一个字符串去跑一下最长回文算法,然后和①一样的处理即可,当然代码会更简单,而且时间复杂度上也比①优