http://acm.hdu.edu.cn/showproblem.php?pid=3613

每个字符都有一个权值,将一个字符串分成两半,如果某一半是回文串就把所有的字符权值加起来,否则当0来处理,问最大值会是多少。

这题很明显是判断前后缀的回文串然后用O(n)的时间遍历取最大值。

问题在于如何判断是前后缀的最大回文串,对于回文串,就很自然而然的想到Manacher算法,对于每一个点,如果以它为中心的回文字符串和最前端接上,他就是一个前缀的回文串,如果和后面接上就是一个后缀的回文串,将所有的这些信息加入到一个数组中之后询问即可。

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define LL long long
#define ULL unsigned long long
const int maxn = 5e5 + ;
const int INF = 0x3f3f3f3f;
int N,M,tmp,K;
int value[];
char str[maxn];
char Ma[maxn * ];
int Mp[maxn * ];
bool cut[maxn][];
LL sum[maxn];
void Manacher(char *x,int n){
int l = ;
Ma[l++] = '$'; Ma[l++] = '#';
for(int i = ; i < n ; i ++){
Ma[l++] = x[i];
Ma[l++] = '#';
}
int mx = ,id = ;
for(int i = ; i < l ; i ++){
Mp[i] = mx > i?min(mx - i,Mp[ * id - i]):;
while(i + Mp[i] < l && Ma[i + Mp[i]] == Ma[i - Mp[i]]) Mp[i]++;
if(Mp[i] + i > mx){
mx = Mp[i] + i;
id = i;
}
}
}
int main()
{
int T; scanf("%d",&T);
while(T--){
Mem(cut,);
For(i,,) Sca(value[i]);
scanf("%s",str);
int len = strlen(str);
Manacher(str,len);
sum[] = ;
for(int i = ; i <= len; i ++) sum[i] = sum[i - ] + value[str[i - ] - 'a'];
for(int i = ; i < len * + ; i ++){
if(i - Mp[i] == ) cut[Mp[i] - ][] = ;
if(i + Mp[i] == len + len + ) cut[Mp[i] - ][] = ;
}
LL ans = -;
For(i,,len - ){
LL s = ;
if(cut[i][]) s += sum[i];
if(cut[len - i][]) s += sum[len] - sum[i];
ans = max(ans,s);
}
Prl(ans);
}
return ;
}

当我们想到解决回文串问题的时候将字符串反转,我们就可以考虑用到扩展KMP算法了。

将原字符串s1进行反转之后得到的s2,当s1作为模式串,s2作为主串进行匹配时,得到的是s2的所有后缀与s1的前缀的最大匹配值,也就是说明当i == extend1[l - i]的时候,s1的前i个字符和s1的后i个字符相等,也就是说前i个字符是s的回文前缀。

同理,当s2作为模式串与s1匹配的时候,得到的是s1的所有后缀与s2的前缀的最大匹配值,当extend2[] == l - i 的时候就是s的回文后缀。

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
#define Vec Point
typedef vector<int> VI;
const double eps = 1e-;
const int maxn = 5e5 + ;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + ;
int N,M,tmp,K;
int value[];
char s1[maxn];
char s2[maxn];
int next1[maxn],next2[maxn];
int extend1[maxn],extend2[maxn];
bool cut[maxn][];
LL sum[maxn];
void EKMP_Pre(char x[],int m,int *next){
int j = ;
next[] = m;
while(j + < m && x[j] == x[j + ]) j ++;
next[] = j;
int k = ;
for(int i = ; i < m ; i++){
int p = next[k] + k - ;
int l = next[i - k];
if(i + l - < p){
next[i] = l;
}else{
int j = max(,p - i + );
while(i + j < m && x[i + j] == x[j]) j ++;
k = i;
next[i] = j;
}
}
}
void EKMP(char x[],int m,char y[],int n,int *next,int *extend){
EKMP_Pre(x,m,next);
int j = ;
while(x[j] == y[j]) j ++;
extend[] = j;
int k = ;
for(int i = ; i < n ; i ++){
int p = extend[k] + k - ;
int l = next[i - k];
if(l + i < p + ) extend[i] = l;
else{
int j = max(,p - i + );
while(i + j < n && j < m && y[i + j] == x[j]) j++;
k = i;
extend[i] = j;
}
}
}
int main()
{
int T; Sca(T);
while(T--){
Mem(cut,);
For(i,,) Sca(value[i]);
scanf("%s",s1); int l = strlen(s1);
for(int i = ; i < l ; i ++) s2[l - i - ] = s1[i];
EKMP(s1,l,s2,l,next1,extend1);
EKMP(s2,l,s1,l,next2,extend2);
for(int i = ; i <= l ; i ++) sum[i] = sum[i - ] + value[s1[i - ] - 'a'];
LL ans = ;
for(int i = ; i < l; i ++){
LL s = ;
if(extend1[l - i] == i) s += sum[i];
if(extend2[i] == l - i) s += sum[l] - sum[i];
ans = max(ans,s);
}
Prl(ans);
}
#ifdef VSCode
system("pause");
#endif
return ;
}

虽然KMP算法是比较基础的算法,但是本题kmp算法在我看来是最难想到的。

与EXKMP一样,将字符串反转形成s1,s2之后,用s1作为模式串与s2匹配,当匹配完成后,指向s2的指针一定是处于末尾的,此时指向s1的指针就是与s2的最大前缀匹配,依据next的特性,我们将前缀k往前跳的每一个点也必然是s1的前缀和s2的后缀匹配的点,也就是s的回文前缀长度。

同理,我们处理出s的回文后缀长度即可。

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
#define Vec Point
typedef vector<int> VI;
const double eps = 1e-;
const int maxn = 5e5 + ;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + ;
int N,M,tmp,K;
int value[];
char s1[maxn];
char s2[maxn];
int Next[maxn];
LL sum[maxn];
int cut[maxn][];
void KMP_Pre(char x[],int m){
int i = ,j = Next[] = -;
while(i < m){
while(j != - && x[i] != x[j]) j = Next[j];
Next[++i] = ++j;
}
}
int KMP(char x[],int m,char y[],int n){
KMP_Pre(x,m);
int i = ,j = ;
while(i < m && j < n){
while(j != - && y[i] != x[j]) j = Next[j];
i++,j++;
}
return j;
}
int main()
{
int T; Sca(T);
while(T--){
Mem(cut,);
For(i,,) Sca(value[i]);
scanf("%s",s1); int l = strlen(s1);
for(int i = ; i < l ; i ++) s2[l - i - ] = s1[i];
for(int i = ; i <= l ; i ++) sum[i] = sum[i - ] + value[s1[i - ] - 'a'];
int k = KMP(s1,l,s2,l);
while(k) cut[k][] = ,k = Next[k];
k = KMP(s2,l,s1,l);
while(k) cut[k][] = ,k = Next[k];
LL ans = -INF;
for(int i = ; i < l; i ++){
LL s = ;
if(cut[i][]) s += sum[i];
if(cut[l - i][]) s += sum[l] - sum[i];
ans = max(ans,s);
}
Prl(ans);
}
#ifdef VSCode
system("pause");
#endif
return ;
}

HDU3613 Manacher//EXKMP//KMP的更多相关文章

  1. KMP && Manacher && 扩展KMP整理

    KMP算法: kmp示例代码: void cal_next(char *str, int *next, int len) { next[0] = -1;//next[0]初始化为-1,-1表示不存在相 ...

  2. Gym - 101981M The 2018 ICPC Asia Nanjing Regional Contest M.Mediocre String Problem Manacher+扩增KMP

    题面 题意:给你2个串(长度1e6),在第一个串里找“s1s2s3”,第二个串里找“s4”,拼接后,是一个回文串,求方案数 题解:知道s1和s4回文,s2和s3回文,所以我们枚举s1的右端点,s1的长 ...

  3. 学习系列 - 马拉车&扩展KMP

    Manacher(马拉车)是一种求最长回文串的线性算法,复杂度O(n).网上对其介绍的资料已经挺多了的,请善用搜索引擎. 而扩展KMP说白了就是是求模式串和主串的每一个后缀的最长公共前缀[KMP更像是 ...

  4. 214. Shortest Palindrome

    题目: Given a string S, you are allowed to convert it to a palindrome by adding characters in front of ...

  5. 【模板(们)】noip前热身练习(更新中...)

    分块+莫队 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; ...

  6. 2013暑假江西联合训练赛 -- by jxust_acm 解题报告

    第6题是利用周期性求解, 第7题是 (总的序列长度-最长的满足要求的序列长度) 第8题是 设定起点,可以找到最早出现的不满足条件,然后后面都是不满足的,利用队列求解这个过程 大神给的简单,精炼的题解. ...

  7. 【扯淡篇】SDOI2018丶一轮游丶记

    --某不知名蒟蒻的SDOI2018 R1退役场游记&&OI生涯总结 真的是混不下去了. 进队是不可能的, 进队是不可能进队的. 这辈子不可能进队的. 刷题又不会刷 就是靠打表找规律这种 ...

  8. noip模拟42

    A. 卷 发现乘积足以爆 \(long\) \(long\),但是数据随机,可以略忽略精度问题 一个快速降低数的级别的方法是取对数,由于有性质 \(log(x * y)=logx+logy\),合并时 ...

  9. HDU3613 Best Reward —— Manacher算法 / 扩展KMP + 枚举

    题目链接:https://vjudge.net/problem/HDU-3613 Best Reward Time Limit: 2000/1000 MS (Java/Others)    Memor ...

随机推荐

  1. 第六周分析Linux内核创建一个新进程的过程

    潘恒 原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 task_struct结构: ...

  2. linux 第七周 总结及实验

    姬梦馨 原创作品 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 第七周 Linux内核如何装载和启动一 ...

  3. ELF分析 实践

    分析ELF文件: 首先编写一个.c文件,并使用gcc -c xx.c -o xx.o 对其进行编译. 我在这个实践中是ELF_1.o文件. 接下来输入ls -l ELF_1.o 查看重定位文件的信息. ...

  4. idea 设置注释

    idea和eclipse的注释还是有一些差别的. idea: 类头注释:打开file->setting->Editor->Filr and Code Templates->In ...

  5. github 心得体会

     https://github.com/xu123/text 学习了很多知识感觉很有趣 git config :配置git git add:更新working directory中的文件至stagin ...

  6. XSS编码与绕过

     XSS编码与绕过 0x00 背景 对于了解web安全的朋友来说,都知道XSS这种漏洞,其危害性不用强调了.一般对于该漏洞的防护有两个思路:一是过滤敏感字符,诸如[<,>,script,' ...

  7. Linux下DB2命令学习及整理

    DB2相关数据库命令 1.数据库实例的启动首先要启动数据库的实例,即切换到db2inst1用户(注:db2inst1用户为当前数据库的实例),然后执行db2start启动数据库的实例 [root@lo ...

  8. CSS实现水平垂直同时居中的6种思路

    前面的话 水平居中和垂直居中已经单独介绍过,本文将介绍水平垂直同时居中的6种思路 水平对齐+行高 [思路一]text-align + line-height实现单行文本水平垂直居中 <style ...

  9. BZOJ4785 ZJOI2017树状数组(概率+二维线段树)

    可以发现这个写挂的树状数组求的是后缀和.find(r)-find(l-1)在模2意义下实际上查询的是l-1~r-1的和,而本来要查询的是l~r的和.也就是说,若结果正确,则a[l-1]=a[r](mo ...

  10. ZOJ2836-Number Puzzle-容斥原理

    依次考虑一个数的倍数,两个数的倍数(lcm),三个数的倍数(lcm)... 会发现有这么一个规律,奇数个数时要加上情况数,偶数个数时要减去情况数. 一种只有10个数,用二进制枚举所有情况即可. #in ...