HDU 3613 Best Reward(扩展kmp)

Description
给出一个只由小写字母组成的字符串以及每个小写字母的价值,现要将这个字符串分成两半,如果某一半是回文串则将累加这一半串的价值(价值即为这个串中每个字符的价值之和),问能得到的最大价值
Input
第一行为一整数T表示用例组数,每组用例首先输入26个整数表示a到z这26个小写字母的价值,之后输入长度不超过500000的一个字符串
Output
对于每组用例,输出将该字符串分成两半后能得到的最大价值
Sample Input
2
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
aba
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
acacac
Sample Output
1
6
Solution
问题在于如果快速判断a串的前缀和后缀是否是回文串,这里只说前缀(因为后缀就是反串的前缀),考虑串a与其反串b的匹配,如果a的某个前缀是回文串,那么因为其反串是b串的一个后缀,所以只要extend[i]=len-i,那么前缀a[0~i]就是一个回文串,同理,在b与a的匹配中,如果extend[i]=len-i,那么后缀a[len-1-i,len-1]就是一个回文串,做两边扩展kmp通过extend数组可以得到以第i个字符结尾或者开始的前后缀是否为回文串,之后O(n)枚举切割点更新最大价值即可(需要预处理出价值前缀和)
Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 555555
char a[maxn],b[maxn];
int T,v[33],sum[maxn],flag1[maxn],flag2[maxn],nex[maxn],extend[maxn];
void extend_kmp(char *a,char *b) 
{
    int i,j,k,la=strlen(a),lb=strlen(b);
    for(i=0;i+1<la&&a[i+1]==a[i];i++);
    nex[1]=i;
    k=1;
    for(i=2;i<la;i++) 
    {
        int len=k+nex[k];
        nex[i]=min(nex[i-k],max(0,len-i));
        while(i+nex[i]<la&&a[nex[i]]==a[i+nex[i]])nex[i]++;
        if(i+nex[i]>k+nex[k])k=i;
    }
    for(i=0;i<la&&i<lb&&a[i]==b[i];i++);
    extend[0]=i;
    k=0;
    for(i=1;i<lb;i++) 
    {
        int len=k+extend[k];
        extend[i]=min(nex[i-k],max(0,len-i));
        while(i+extend[i]<la&&i+extend[i]<lb&&a[extend[i]]==b[extend[i]+i])
            extend[i]++;
        if(k+extend[k]<i+extend[i])k=i;
    }
}
int main()
{   
    scanf("%d",&T);
    while(T--)
    {
        memset(flag1,0,sizeof(flag1));
        memset(flag2,0,sizeof(flag2));
        for(int i=0;i<26;i++)scanf("%d",&v[i]);
        scanf("%s",a);
        int len=strlen(a);
        for(int i=0;i<len;i++)b[i]=a[len-1-i];
        sum[0]=v[a[0]-'a'];
        for(int i=0;i<len;i++)sum[i]=sum[i-1]+v[a[i]-'a'];
        extend_kmp(a,b);
        for(int i=0;i<len;i++)if(extend[i]==len-i)flag1[len-1-i]=1;
        extend_kmp(b,a);
        for(int i=0;i<len;i++)if(extend[i]==len-i)flag2[i]=1;
        int ans=v[a[0]-'a'];
        for(int i=0;i<len-1;i++)
        {
            int temp=flag1[i]*sum[i]+flag2[i+1]*(sum[len-1]-sum[i]);
            ans=max(ans,temp);
        }
        printf("%d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(HDU 3613 Best Reward(扩展kmp))