HDU 3613 Best Reward(KMP+思维||manacher算法)

来源:互联网 发布:visio 2015 mac破解版 编辑:程序博客网 时间:2024/05/11 17:05

题目链接:点击打开链接

题意:26个小写英文字母都有一个价值,给你一个字符串,将该字符串切成左右两半,对于每一半,如果是回文串,就获得该子串的字母价值之和,否则该子串的价值为0。求出将字符串切成两半后能够获得的最大价值。

思路一:KMP+思维。

思路二:manacher算法(马拉车算法)

具体思路明天写......

咳咳,回来写思路了......

主要介绍KMP+思维实现,基于manacher算法的方法和代码略。

思路:

首先要熟悉回文串的基本特征,正反读,字符串是一样的!!!题目说左右两半都是回文串,那么可以考虑把字符串前缀和后缀里所有的回文串找到,怎么找呢?先拿前缀考虑,如果把串反转,那么前缀的回文序列,在反转串的后缀里一定出现,利用KMP算法匹配原串前缀和反转串后缀,一定会匹配出前缀的最长回文序列,同理,匹配反转串前缀和原串后缀,可以匹配出原串的最长回文后缀。得到的是最长的回文串,可是我们需要标记出所有的呀,咋办?

思维处理出现了,还是拿前缀为例,已经匹配出的最长回文前缀设为s,如果s里面还存在一个最长的回文前缀t,t的长度肯定是小于s,而且,由于s回文,那么从s末尾向前读,读t的长度个字符,依次读取到的序列和t一定相同,这段序列记为tt,由于t回文,t和tt相同,那么tt是回文串,所以t和tt匹配,由此可以联想到Next函数,那么Next[s的长度数值]会是t的长度数值吗?可以利用反证法,假设利用Next函数得到的长度大于此长度,序列记做u,即u不是回文串,与此匹配的后缀v肯定是与u相同,由于s是回文串,v还要反向和u一致,所以v是回文串,与u不是回文串矛盾,

所以由s的Next得到的位置即为下一个最长回文前缀的位置,以此类推,得到所有的回文前缀。后缀思路一致,分析略。

分析到这,剩下的就很简单了,枚举分割点,计算左右价值总和,更新最大值。

// HDU 3613 Best Reward 运行/限制:93ms/1000ms#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;#define INF 0x3f3f3f3fchar s[500005],p[500005];//原串 反向串int Next[500005];int v[26],sum[500005];//字母对应的权值 原串权值前缀和int pre[500005], pos[500005];//记录前缀为回文串的情况 记录后缀为回文串的情况(位置标号为反向串下标)void getNext(char s[], int n) {int i, j;i = -1, j = 0;Next[0] = -1;while (j < n) {if (i == -1 || s[i] == s[j]) {i++; j++;Next[j] = i;//不优化}else {i = Next[i];}}}int kmp(char s[], char p[], int n) {int i, j;i = j = 0;getNext(s, n);while (j < n) {if (i == -1 || p[j] == s[i]) {i++; j++;}else {i = Next[i];}}return i;}int main(){int t, len, k, re, ans;scanf("%d", &t);while (t--) {re = -INF;for (int i = 0; i < 26; i++) {scanf("%d", &v[i]);}scanf("%s", s);len = strlen(s);memset(pre, 0, sizeof(pre));memset(pos, 0, sizeof(pos));p[0] = s[len - 1];sum[0] = v[s[0] - 'a'];for (int i = 1; i < len; i++) {p[i] = s[len - i - 1];sum[i] = sum[i - 1] + v[s[i] - 'a'];}p[len] = '\0';k = kmp(s, p, len);while (k > 0) {//记录前缀回文串pre[k - 1] = 1;k = Next[k];}k = kmp(p, s, len);while (k > 0) {//记录后缀回文串pos[k - 1] = 1;k = Next[k];}for (int i = 0; i < len - 1; i++) {//枚举前半部分终点ans = 0;if (pre[i]) ans += sum[i];if (pos[len - 2 - i]) ans += sum[len - 1] - sum[i];re = max(re, ans);}printf("%d\n", re);}    return 0;}


原创粉丝点击