Best Reward (manacher)
题目大意
给你一个字符串,每个字母都有一个值,每你需要找一个切割点,如果切割后一边是回文串就可以加上那边的值。求和的最大值。
思路
我们先用manacher求出每个字符作为中心点的回文串长度。
求出原串中的字母值的前缀和。
枚举原串中的切割点(切割处为切割点右边)。
寻找切割点的左右的中心点,看左右两边是否为回文串(中心点刚好包含整个左串或右串)。
前缀和求值。
AC
对于怎么寻找中心点
通过大量找规律得:
原串从0开始时,
左字串的中心点 在辅助串中的下标 为 切割点在原串的下标 +2
右字串的中心点 在辅助串中的下标 为 切割点在原串的下标 +2+原串长度
CODE
#include<bits/stdc++.h>
using namespace std;
const int maxn=500005;
int t,n,m,ans;
int v[30],b[maxn*2],sum[maxn*2],len[maxn*2];
char pose[maxn*2],a[maxn];
void dispose()//处理辅助串
{
for(int i=1;i<=n*2+1;i+=2)
{
pose[i+1]=a[i/2];
pose[i]='#';
}
m=2*n+1;
pose[0]='@';
pose[m+1]='$';
}
void manacher()//马拉车
{
memset(len,0,sizeof(len));//记得清0
int po,p;
po=p=0;
for(int i=1;i<=m;i++)
{
if(p>i) len[i]=min(p-i,len[2*po-i]);
else len[i]=1;
while(pose[i-len[i]]==pose[i+len[i]]) len[i]++;
if(len[i]+i>p)
{
p=len[i]+i;
po=i;
}
}
}
int main()
{
scanf("%d",&t);
while(t--)
{
ans=0;
for(int i=0;i<26;i++) scanf("%d",&v[i]);
cin>>a;
n=strlen(a);
sum[0]=v[a[0]-'a'];
for(int i=1;i<n;i++)//前缀和
sum[i]=sum[i-1]+v[a[i]-'a'];
dispose();
manacher();
n=strlen(a);
for(int i=0;i<n-1;i++)//重点
{
int tmp=0,num;
num=len[i+2]-1;//左子串的中心点的回文长度
if(num==i+1) tmp+=sum[i];
num=len[i+n+2]-1;//右子串的中心点的回文长度
if(num==n-i-1) tmp+=(sum[n-1]-sum[i]);
ans=max(ans,tmp);
}
printf("%d\n",ans);
}
}
完美撒花