题意
首先给你一个长度为26的数组,表示的是26个字母代表的权值,之后给你一串字符串,之后给你一串字符,你需要将这个字符变成切成两半,如果你切下来的部分是回文串的话,那么你将得到这部分的权值,如果他不是回文串的话他的权值就是0,现在问你怎样切使得切下来的两个串的权值最大。
思路
首先将原串
c
h
ch
ch翻转出来变成
s
h
sh
sh,这个时候你会发现,如果一个串是回文串的话,原串翻转出来的串和原串其实相等的,那么他切成两个部分,就会有原串的前缀其实是后来串的后缀,那么就把他传化成了一个exkmp了,我们将原串和翻转串都做一次exkmp,之后枚举切的位置,之后贪心的去取就好了。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 500000 + 10;
int Rank[30] , nex[maxn] , ex1[maxn] , sum[maxn] ,ex2[maxn];
void EKMP(string s,string t,int ex[])//s[]为主串,t[]为模式串
{
int i,j,p,l;
int len=t.size();
int len1=s.size();
memset(nex,0,sizeof(nex));
memset(ex,0,sizeof(ex));
nex[0] = len;
j = 0;
while(j+1 < len && t[j] == t[1+j])j++;
nex[1]=j;
int a=1;
for(int i=2;i<len;i++)
{
p = nex[a]+a-1;
l = nex[i-a];
if(i + l < p + 1 )nex[i]=l;
else
{
j=max(0,p-i+1);
while(i+j<len&&t[i+j]==t[0+j])j++;
nex[i]=j;
a=i;
}
}
j = 0;
while(j < len1 && j < len && s[j] == t[j])j++;
ex[0]=j;
a=0;
for(i = 1;i < len1;i++)
{
p = ex[a]+a-1;
l = nex[i-a];
if(l + i < p + 1)ex[i] = l;
else
{
j = max(0,p-i+1);
while(i + j < len1 && j < len && s[i+j] == t[j])j++;
ex[i]=j;
a=i;
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(sum,0,sizeof(sum));
string ch , sh;
for(int i = 0 ; i < 26 ; i++) scanf("%d",&Rank[i]);
cin>>ch;
sum[0] = 0;
for(int i = 0 ; i < ch.size() ; i ++) sum[i+1] = sum[i] + Rank[ch[i]-'a'];
sh = ch;
reverse(sh.begin(),sh.end());
EKMP(ch,sh,ex1);
EKMP(sh,ch,ex2);
int MAX = -1 , ans = 0 ;
for(int i = 1 ; i < sh.size() ; i++)
{
ans = 0;
int j = sh.size() - i;
if(i + ex1[i] == sh.size()) ans += sum[sh.size()] - sum[i]; // 首先判断[i~n]的位置是不是回文的。
if(j + ex2[j] == sh.size()) ans += sum[i]; // 然后去另外一边观察[0~i]的位置
MAX = max(MAX,ans);
}
cout<<MAX<<endl;
}
}