[Manacher][HDU3613][Best Reward]

题意:
将一段字符串 分割成两个串
如果分割后的串为回文串,则该串的价值为所有字符的权值之和(字符的权值可能为负数),否则为0。
问如何分割,使得两个串权值之和最大

思路:
裸的:
枚举分割点,计算,O(n) 判断是否回文
总复杂度O(n^2)
优化:
利用Manacher的预处理 O(1)判断是否回文
复杂度O(n)

//Manacher
/*  
** 求最长回文子串 
*/ 
#include <cstdio>
#include <cstring> 
#include <iostream>
#include <cstdlib>
using namespace std;
const int MAXN=550000;
char Ma[MAXN*2];
int Mp[MAXN*2];
void Manacher(char s[],int len){
    int l=0;
    Ma[l++]='$';
    Ma[l++]='#';
    for(int i=0;i<len;i++){
        Ma[l++]=s[i];
        Ma[l++]='#';
    } 
    Ma[l]=0;
    int mx=0,id=0;
    for(int i=0;i<l;i++){
        Mp[i]=mx>i?min(Mp[2*id-i],mx-i):1;
        while(Ma[i+Mp[i]]==Ma[i-Mp[i]]) Mp[i]++;
        if(i+Mp[i]>mx){
            mx=i+Mp[i];
            id=i;
        } 
    }
} 
/*
 * abaaba
 * i:              0 1  2  3  4  5  6  7  8  9 10 11 12 13
 *Ma[i]:           $ #  a  #  b  #  a  #  a  # b  #  a  #  
 *Mp[i]:           1 1  2  1  4  1  2  7  2  1 4  1  2  1
 */
char s[MAXN];
int sum[MAXN*2];
int v[30];
void input()
{
    for(int i=1;i<=26;i++)
    scanf("%d",&v[i]);
    scanf("%s",s);
}
void get_sum()
{
    int len=strlen(Ma);
    for(int i=1;i<len;i++)
    {
        sum[i]=sum[i-1];
        if('a'<=Ma[i]&&Ma[i]<='z')
        sum[i]+=v[Ma[i]-'a'+1];
//      printf("%d %d
",i,sum[i]);
    }
}
void solve()
{
    int len=strlen(s);
    Manacher(s,len);
    get_sum();
    len=strlen(Ma);
    int ans=-0x3f3f3f3f;
    for(int i=3;i<len-1;i=i+2)
    {
        int a1=0,a2=0;
        //判断之前的串是不是回文串
        if((((2+(i-1))/2)+Mp[(2+(i-1))/2]-1)>=(i-1))
        a1=sum[i]-sum[1];
        if(((len+i-1)/2-Mp[(len+i-1)/2]+1)<=(i+1))
        a2=sum[len-2]-sum[i];
        if(ans<a1+a2)
        ans=a1+a2;
    }
    cout<<ans<<endl;
}
int main()
{
//  freopen("a.in","r",stdin);
    int T;
    cin>>T;
    while(T--)
    {
        input();
        solve();
    }
}
原文地址:https://www.cnblogs.com/zy691357966/p/5480314.html