HDU 3613 Manacher应用

3 篇文章 0 订阅
2 篇文章 0 订阅

题意:上面的数字是26个字母的价值,接下来26个英文字母组成的字符串,将它分成两份,如果分后的串是回文串,则这个串的价值是这个串的所有字母的和,如果不是回文串则这个串价值为0。求最大价值和。

看到回文串,第一时间可以想到manacher算法。只要得到了以第 i 位为中心的最长回文串长度的一半ansi,便不难解决此题。

我们可以枚举切割点 i ,容易求出左子串的中心lmid和右子串的中心 rmid ,若要判断左子串是否回文,只需判断 lmid+anslmid 是否等于 i 即可,右子串同理,不过加变成减。

如果某一子串回文,我们就要计算它的价值和。这时候每一次都计算显然会很慢,我们可以用部分和Θ(n)预处理,要用的时候直接 Θ(1) 计算。

到这里问题就基本解决了,时间复杂度也只是线性的。

#include <algorithm>
#include <climits>
#include <cstdio>
#include <cstring>

using namespace std;

const int charset = 26;
const int maxlen = 5e5 + 9;

int value[charset + 5];
int presum[maxlen << 1];

char st[maxlen << 1], tmp[maxlen << 1];
int len;

int f[maxlen << 1];

void init() {
    for(int i = 0; i < charset; i++) scanf("%d", &value[i]);

    scanf("%s", tmp);
    st[0] = '!';
    len = 1;
    int tlen = strlen(tmp);

    for(int i = 0; i < tlen; i++) {
        st[len++] = '#';
        st[len++] = tmp[i];
    }

    st[len++] = '#';
    st[len++] = '?';
}

void get_sum() {
    presum[0] = presum[1] = 0;
    presum[2] = value[st[2] - 'a'];

    for(int i = 3; i < len; i++) {
        presum[i] = presum[i - 1];

        if(st[i] != '#') presum[i] += value[st[i] - 'a'];
    }
}

void manacher() {
    f[0] = f[1] = 0;
    int p = 1;

    for(int i = 2; i < len; i++) {
        f[i] = max(0, min(f[(p << 1) - i], p + f[p] - i));

        while(st[i - f[i] - 1] == st[i + f[i] + 1]) ++f[i];

        if(i + f[i] > p + f[p]) p = i;
    }
}

void solve() {
    init();
    get_sum();
    manacher();
    int ans = INT_MIN;
    int left_mid = 2;
    int right_mid = ((len - 4) >> 1) + 3;

    for(int i = 3; i + 2 < len; i += 2) {
        int s = 0;

        if(left_mid + f[left_mid] == i) s += presum[i];

        if(right_mid - f[right_mid] == i) s += presum[len - 3] - presum[i];

        ++left_mid;
        ++right_mid;

        ans = max(ans, s);
    }

    printf("%d\n", ans);
}

int main() {
    freopen("hdu3613.in", "r", stdin);
    freopen("hdu3613.out", "w", stdout);
    int caset;
    scanf("%d", &caset);

    for (int casei = 0; casei < caset; casei++) solve();

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值