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

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

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

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

  1. #include <map>
  2. #include <set>
  3. #include <ctime>
  4. #include <cmath>
  5. #include <queue>
  6. #include <stack>
  7. #include <vector>
  8. #include <string>
  9. #include <cstdio>
  10. #include <cstdlib>
  11. #include <cstring>
  12. #include <sstream>
  13. #include <iostream>
  14. #include <algorithm>
  15. #include <functional>
  16. using namespace std;
  17. #define For(i, x, y) for(int i=x;i<=y;i++)
  18. #define _For(i, x, y) for(int i=x;i>=y;i--)
  19. #define Mem(f, x) memset(f,x,sizeof(f))
  20. #define Sca(x) scanf("%d", &x)
  21. #define Scl(x) scanf("%lld",&x);
  22. #define Pri(x) printf("%d\n", x)
  23. #define Prl(x) printf("%lld\n",x);
  24. #define LL long long
  25. #define ULL unsigned long long
  26. const int maxn = 5e5 + ;
  27. const int INF = 0x3f3f3f3f;
  28. int N,M,tmp,K;
  29. int value[];
  30. char str[maxn];
  31. char Ma[maxn * ];
  32. int Mp[maxn * ];
  33. bool cut[maxn][];
  34. LL sum[maxn];
  35. void Manacher(char *x,int n){
  36. int l = ;
  37. Ma[l++] = '$'; Ma[l++] = '#';
  38. for(int i = ; i < n ; i ++){
  39. Ma[l++] = x[i];
  40. Ma[l++] = '#';
  41. }
  42. int mx = ,id = ;
  43. for(int i = ; i < l ; i ++){
  44. Mp[i] = mx > i?min(mx - i,Mp[ * id - i]):;
  45. while(i + Mp[i] < l && Ma[i + Mp[i]] == Ma[i - Mp[i]]) Mp[i]++;
  46. if(Mp[i] + i > mx){
  47. mx = Mp[i] + i;
  48. id = i;
  49. }
  50. }
  51. }
  52. int main()
  53. {
  54. int T; scanf("%d",&T);
  55. while(T--){
  56. Mem(cut,);
  57. For(i,,) Sca(value[i]);
  58. scanf("%s",str);
  59. int len = strlen(str);
  60. Manacher(str,len);
  61. sum[] = ;
  62. for(int i = ; i <= len; i ++) sum[i] = sum[i - ] + value[str[i - ] - 'a'];
  63. for(int i = ; i < len * + ; i ++){
  64. if(i - Mp[i] == ) cut[Mp[i] - ][] = ;
  65. if(i + Mp[i] == len + len + ) cut[Mp[i] - ][] = ;
  66. }
  67. LL ans = -;
  68. For(i,,len - ){
  69. LL s = ;
  70. if(cut[i][]) s += sum[i];
  71. if(cut[len - i][]) s += sum[len] - sum[i];
  72. ans = max(ans,s);
  73. }
  74. Prl(ans);
  75. }
  76. return ;
  77. }

当我们想到解决回文串问题的时候将字符串反转,我们就可以考虑用到扩展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的回文后缀。

  1. #include <map>
  2. #include <set>
  3. #include <ctime>
  4. #include <cmath>
  5. #include <queue>
  6. #include <stack>
  7. #include <vector>
  8. #include <string>
  9. #include <cstdio>
  10. #include <cstdlib>
  11. #include <cstring>
  12. #include <sstream>
  13. #include <iostream>
  14. #include <algorithm>
  15. #include <functional>
  16. using namespace std;
  17. #define For(i, x, y) for(int i=x;i<=y;i++)
  18. #define _For(i, x, y) for(int i=x;i>=y;i--)
  19. #define Mem(f, x) memset(f,x,sizeof(f))
  20. #define Sca(x) scanf("%d", &x)
  21. #define Scl(x) scanf("%lld",&x);
  22. #define Pri(x) printf("%d\n", x)
  23. #define Prl(x) printf("%lld\n",x);
  24. #define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
  25. #define LL long long
  26. #define ULL unsigned long long
  27. #define mp make_pair
  28. #define PII pair<int,int>
  29. #define PIL pair<int,long long>
  30. #define PLL pair<long long,long long>
  31. #define pb push_back
  32. #define fi first
  33. #define se second
  34. #define Vec Point
  35. typedef vector<int> VI;
  36. const double eps = 1e-;
  37. const int maxn = 5e5 + ;
  38. const int INF = 0x3f3f3f3f;
  39. const int mod = 1e9 + ;
  40. int N,M,tmp,K;
  41. int value[];
  42. char s1[maxn];
  43. char s2[maxn];
  44. int next1[maxn],next2[maxn];
  45. int extend1[maxn],extend2[maxn];
  46. bool cut[maxn][];
  47. LL sum[maxn];
  48. void EKMP_Pre(char x[],int m,int *next){
  49. int j = ;
  50. next[] = m;
  51. while(j + < m && x[j] == x[j + ]) j ++;
  52. next[] = j;
  53. int k = ;
  54. for(int i = ; i < m ; i++){
  55. int p = next[k] + k - ;
  56. int l = next[i - k];
  57. if(i + l - < p){
  58. next[i] = l;
  59. }else{
  60. int j = max(,p - i + );
  61. while(i + j < m && x[i + j] == x[j]) j ++;
  62. k = i;
  63. next[i] = j;
  64. }
  65. }
  66. }
  67. void EKMP(char x[],int m,char y[],int n,int *next,int *extend){
  68. EKMP_Pre(x,m,next);
  69. int j = ;
  70. while(x[j] == y[j]) j ++;
  71. extend[] = j;
  72. int k = ;
  73. for(int i = ; i < n ; i ++){
  74. int p = extend[k] + k - ;
  75. int l = next[i - k];
  76. if(l + i < p + ) extend[i] = l;
  77. else{
  78. int j = max(,p - i + );
  79. while(i + j < n && j < m && y[i + j] == x[j]) j++;
  80. k = i;
  81. extend[i] = j;
  82. }
  83. }
  84. }
  85. int main()
  86. {
  87. int T; Sca(T);
  88. while(T--){
  89. Mem(cut,);
  90. For(i,,) Sca(value[i]);
  91. scanf("%s",s1); int l = strlen(s1);
  92. for(int i = ; i < l ; i ++) s2[l - i - ] = s1[i];
  93. EKMP(s1,l,s2,l,next1,extend1);
  94. EKMP(s2,l,s1,l,next2,extend2);
  95. for(int i = ; i <= l ; i ++) sum[i] = sum[i - ] + value[s1[i - ] - 'a'];
  96. LL ans = ;
  97. for(int i = ; i < l; i ++){
  98. LL s = ;
  99. if(extend1[l - i] == i) s += sum[i];
  100. if(extend2[i] == l - i) s += sum[l] - sum[i];
  101. ans = max(ans,s);
  102. }
  103. Prl(ans);
  104. }
  105. #ifdef VSCode
  106. system("pause");
  107. #endif
  108. return ;
  109. }

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

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

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

  1. #include <map>
  2. #include <set>
  3. #include <ctime>
  4. #include <cmath>
  5. #include <queue>
  6. #include <stack>
  7. #include <vector>
  8. #include <string>
  9. #include <cstdio>
  10. #include <cstdlib>
  11. #include <cstring>
  12. #include <sstream>
  13. #include <iostream>
  14. #include <algorithm>
  15. #include <functional>
  16. using namespace std;
  17. #define For(i, x, y) for(int i=x;i<=y;i++)
  18. #define _For(i, x, y) for(int i=x;i>=y;i--)
  19. #define Mem(f, x) memset(f,x,sizeof(f))
  20. #define Sca(x) scanf("%d", &x)
  21. #define Scl(x) scanf("%lld",&x);
  22. #define Pri(x) printf("%d\n", x)
  23. #define Prl(x) printf("%lld\n",x);
  24. #define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
  25. #define LL long long
  26. #define ULL unsigned long long
  27. #define mp make_pair
  28. #define PII pair<int,int>
  29. #define PIL pair<int,long long>
  30. #define PLL pair<long long,long long>
  31. #define pb push_back
  32. #define fi first
  33. #define se second
  34. #define Vec Point
  35. typedef vector<int> VI;
  36. const double eps = 1e-;
  37. const int maxn = 5e5 + ;
  38. const int INF = 0x3f3f3f3f;
  39. const int mod = 1e9 + ;
  40. int N,M,tmp,K;
  41. int value[];
  42. char s1[maxn];
  43. char s2[maxn];
  44. int Next[maxn];
  45. LL sum[maxn];
  46. int cut[maxn][];
  47. void KMP_Pre(char x[],int m){
  48. int i = ,j = Next[] = -;
  49. while(i < m){
  50. while(j != - && x[i] != x[j]) j = Next[j];
  51. Next[++i] = ++j;
  52. }
  53. }
  54. int KMP(char x[],int m,char y[],int n){
  55. KMP_Pre(x,m);
  56. int i = ,j = ;
  57. while(i < m && j < n){
  58. while(j != - && y[i] != x[j]) j = Next[j];
  59. i++,j++;
  60. }
  61. return j;
  62. }
  63. int main()
  64. {
  65. int T; Sca(T);
  66. while(T--){
  67. Mem(cut,);
  68. For(i,,) Sca(value[i]);
  69. scanf("%s",s1); int l = strlen(s1);
  70. for(int i = ; i < l ; i ++) s2[l - i - ] = s1[i];
  71. for(int i = ; i <= l ; i ++) sum[i] = sum[i - ] + value[s1[i - ] - 'a'];
  72. int k = KMP(s1,l,s2,l);
  73. while(k) cut[k][] = ,k = Next[k];
  74. k = KMP(s2,l,s1,l);
  75. while(k) cut[k][] = ,k = Next[k];
  76. LL ans = -INF;
  77. for(int i = ; i < l; i ++){
  78. LL s = ;
  79. if(cut[i][]) s += sum[i];
  80. if(cut[l - i][]) s += sum[l] - sum[i];
  81. ans = max(ans,s);
  82. }
  83. Prl(ans);
  84. }
  85. #ifdef VSCode
  86. system("pause");
  87. #endif
  88. return ;
  89. }

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. 初学Java必写的小程序。

    1.矩形面积,周长封装测试. /** * @author Administrator *封装好的矩形类 *自己私有的长宽属性 *开放 求面积求周长的方法 和设置长宽的方法 */ public clas ...

  2. Linux列举所有隐藏文件

    ll 命令是 ls -l的缩写 ls -a是列举所有(all)文件,包含隐藏文件,以.开头的文件. ls -l是以列表(list)方式列举文件. http://bbs.chinaunix.net/th ...

  3. DELPHI XE10,JSON 生成和解析,再利用INDYHTTP控件POST

    Delphi XE10,Json 生成和解析,再利用indyhttp控件Post 年09月20日 :: 阅读数: --不多说,直接上代码 procedure TFrmMain.Brand; var J ...

  4. PDB自动启动以及Oracle Pfile的参数修改示范

    1. Oracle12c 可以使用PDB的模式进行创建, 但是他一般不会自动启动,所以可以穿件一个触发器进行处理 创建语句 CREATE TRIGGER open_all_pdbs AFTER STA ...

  5. Delphi编程中动态菜单要点归纳

      一.创建菜单并添加项目 在设计程序时,有时需要动态创建菜单, 通常使用以下的语句: PopupMenu1 := TPopupMenu.Create(Self);  Item := TMenuIte ...

  6. html 腳本

    腳本引入: <script type="text/script"> document.write("hello")</script> 腳 ...

  7. SpringMVC @RequestBody的使用

    @RequestBody的作用 @RequestBody用于读取Request请求的body数据,然后利用SpringMVC配置的HttpMessageConverter对数据进行转换,最后把转换后的 ...

  8. selenium之调用Javascript

    selenium调用Javascript使用方法: driver.execute_script(js) 使用JS获取元素文本值,代码片段如下: ...... js = "return $(' ...

  9. Bicriterial routing 双调路径 HYSBZ - 1375(分层最短路)

    Description 来越多,因此选择最佳路径是很现实的问题.城市的道路是双向的,每条道路有固定的旅行时间以及需要支付的费用.路径由连续的道路组成.总时间是各条道路旅行时间的和,总费用是各条道路所支 ...

  10. MT【46】不动点,稳定点几何直观

    评:不动点概念在数列的一类题中也是非常有用的.