HDU - 3613 Best Reward(KMP和拓展KMP)

题目大意:给你26个字符对应的价值和一个字符串,要求你将字符串分成两个部分。分开后,如果那部分是回文的话,那么价值就是该部分字符的价值和,如果不是回文,价值为0,问分开后的最大价值和

解题思路:可以用KMP做也可以用拓展KMP做

KMP做的话,假设原字符串是s1,然后将字符串倒转变成s2,先求s1的后缀和s2的前缀匹配的部分,这部分是s1的后缀的回文部分,然后再通过next数组,找出所有的回文,并标记
接下来求出s2的后缀和s1的前缀的匹配部分,这部分就是s1前缀的回文部分了,同理,通过next数组求出所有的回文部分,并标记
接下来就是枚举每个位置了

#include <cstdio>
#include <cstring>
const int INF = 0x3f3f3f3f;
const int N = 500010;
int val[30], sum[N];
int next[N];
int len, test;
char s1[N], s2[N];
int pre[N], suf[N];

void init() {
    for (int i = 0; i < 26; i++)
        scanf("%d", &val[i]);
    scanf("%s", s1);
    len = strlen(s1);
    for (int i = 0; i < len; i++) {
        s2[i] = s1[len - i - 1];
        sum[i + 1] = sum[i] + val[s1[i] - 'a'];
    }
}

void getNext(char *s1, int *next) {
    int i = 0, j = -1;
    next[0] = -1;
    while (i < len) {
        if (j == -1 || s1[i] == s1[j]) next[++i] = ++j;
        else j = next[j];
    }
}

int KMP(char *s1, char *s2) {
    int i = 0, j = 0;
    while (i < len) {
        if (j == -1 || s1[j] == s2[i]) {
            j++; i++;
        }
        else j = next[j];
    }
    return j;
}

void solve() {
    getNext(s1, next);
    int t = KMP(s1, s2);
    while (t) {
        pre[t] = test + 1;
        t = next[t];
    }
    getNext(s2, next);
    t = KMP(s2, s1);
    while (t) {
        suf[t] = test + 1;
        t = next[t];
    }
    int ans = -INF, tmp = 0;
    for (int i = 1; i < len; i++) {
        if (pre[i] == test + 1) tmp += sum[i];
        if (suf[len - i] == test + 1) tmp += sum[len] - sum[i];
        if (tmp > ans) ans = tmp;
        tmp = 0;
    }
    printf("%d\n", ans);
}

int main() {
    scanf("%d", &test);
    while (test--) {
        init();
        solve();
    }
    return 0;
}

拓展KMP的话,和上面的差不多,也是翻转后求公共前缀的

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 500010;

char s1[N], s2[N];
int len;
int val[30], sum[N], Next[N], ex1[N], ex2[N];

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

    scanf("%s", s1);
    len = strlen(s1);
    for (int i = 0; i < len; i++) {
        sum[i + 1] = sum[i] + val[s1[i] - 'a'];
        s2[len - i - 1] = s1[i];
    }
}

void getNext(char *s1) {
    Next[0] = len;
    int i;
    for (i = 0; s1[i] == s1[i + 1]; i++);
    Next[1] = i;
    int a = 1;
    for (int k = 2; k < len; k++) {
        int p = a + Next[a] - 1, L = Next[k - a];
        if (k + L - 1 >= p) {
            int j = max(p - k + 1, 0);
            while (k + j < len && s1[j] == s1[k + j]) j++;
            Next[k] = j;
            a = k;
        }
        else Next[k] = L;
    }
}

void EDKMP(char *s1, char *s2, int *ex) {
    getNext(s1);
    int i = 0;
    for (; s1[i] == s2[i] && i < len; i++);
    ex[0] = i;
    int a = 0;
    for (int k = 1; k < len; k++) {
        int p = ex[a] + a - 1, L = Next[k - a];
        if (k + L - 1 >= p) {
            int j = max(p - k + 1, 0);
            while (k + j < len && s1[j] == s2[j + k]) j++;
            ex[k] = j;
            a = k;
        }
        else ex[k] = L;
    }
}

void solve() {
    EDKMP(s1, s2, ex1);
    EDKMP(s2, s1, ex2);
    int ans = 0, tmp = 0;
    for (int i = 1; i < len; i++) {
        if (ex1[len - i] == i) tmp += sum[i];
        if (ex2[i] == len - i) tmp += sum[len] - sum[i];
        if (tmp > ans) ans = tmp;
        tmp = 0;
    }
    printf("%d\n", ans);
}

int main() {
    int test;
    scanf("%d", &test);
    while (test--) {
        init();
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值