kuangbin专题十六 HDU 3613(Manacher+贪心)

本文介绍了一个字符串处理问题的解决方案,通过Manacher算法判断回文子串,并结合贪心策略找到最大价值的切分点。使用特殊字符扩展原字符串以简化边界处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:
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);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值