题意:
26个字母都有一个价值,给你一个字符串,将该字符串切成两份,对于每一份,如果是回文串,就获得该子串的字母价值之和,否则该子串的价值为0。求出将字符串切成两份后能够获得的最大价值。
题解:
Manacher判断回文+贪心剪断那个点。这道题就是套个manacher算法就是用来判断剪下来的这段是否属于回文,重点是贪心这里,我之前用贪心去剪了,但是怎么剪都不对,而且还要判断奇偶的情况,弄了一上午都弄不出来,后来看了别人的发现,这才是学会了manacher算法的人啊,因为我们要剪开这条子串,分成两半,来判断两个是否都为子串,并且价值最大,这就需要贪心了,关键是你怎么判断,而别人是直接用你马拉松增加了不相关符号之后的长度去判断的,跑for循环的时候是用原本字符串长度去跑的,当你判断i(即左边的字符串)的时候,要判断右边的话就用i+len去判断。至于为什么是i+2,len+i+2是因为该算法本身第0个和第一个都是无相关符号。
题外话:
这里有一点奇葩的就是为什么我在算前缀和的时候用strlen(str)会超时,而用len=strlen(str)再放len进去就不超时呢?我不理解啊,一直超时在这里,MBD
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int MAXN=500000+7;
char str[MAXN];//原字符串
char tmp[MAXN<<1];//转换后的字符串
int Len[MAXN<<1];
int sum[MAXN];
int w[30];
//转换原始串
int init()
{
int i,len=strlen(str);
tmp[0]='@';//字符串开头增加一个特殊字符,防止越界
for(i=1;i<=2*len;i+=2)
{
tmp[i]='#';
tmp[i+1]=str[i/2];
}
tmp[2*len+1]='#';
tmp[2*len+2]='$';//字符串结尾加一个字符,防止越界
tmp[2*len+3]=0;
return 2*len+1;//返回转换字符串的长度
}
//Manacher算法计算过程
void MANACHER(int len)
{
int mx=0,ans=0,po=0;//mx即为当前计算回文串最右边字符的最大值
for(int i=1;i<=len;i++)
{
if(mx>i)
Len[i]=min(mx-i,Len[2*po-i]);//在Len[j]和mx-i中取个小
else
Len[i]=1;//如果i>=mx,要从头开始匹配
while(tmp[i-Len[i]]==tmp[i+Len[i]])
Len[i]++;
if(Len[i]+i>mx)//若新计算的回文串右端点位置大于mx,要更新po和mx的值
{
mx=Len[i]+i;
po=i;
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
for(int i=0;i<26;i++)
scanf("%d",&w[i]);
scanf("%s",str);
sum[0]=w[str[0]-'a'];
int len=strlen(str);
for(int i=1;i<len;i++)
sum[i]=sum[i-1]+w[str[i]-'a'];
int l=init();
MANACHER(l);
len=strlen(str);
int ans=0;
for(int i=0;i<len;i++)
{
int q=0;
int L=Len[i+2]-1;
if(L==i+1) q+=sum[i];
L=Len[len+i+2]-1;
if(L==len-i-1) q+=sum[len-1]-sum[i];
if(q>ans)
ans=q;
}
printf("%d\n",ans);
}
}