这个题目是hihoCoder第一周的题目,自己打算从第一周开始做起,不知道能追上多少,更不知道这一篇写完,下一篇会是什么时候。。。

题意很简单。

输入:

3

abababa

aaaabaa

acacdas

输出:

7

5

3

这种经典题目POJ上也有,忘了是第多少道了。

当时我对于Manacher算法不是很理解,其实现在也感觉恍惚。不知道当我写完的时候会不会大彻大悟。

另外,写这篇总结参考了很多大牛博客上的代码,代码可能有雷同,我的锅。但自己的感想是真实的。

第一种方法,直接暴力:

  1. #include <cstdio>
  2. #include <iostream>
  3. #include <string>
  4. using namespace std;
  5.  
  6. void result (string test)
  7. {
  8. int count = test.size();
  9. int start=0,end=count-1,max=0;
  10. int i,j,loop;
  11.  
  12. for(i = start; i < end; i++){
  13. for(j = end; j > start; j--){
  14. for(loop = (j-i)/2 ; loop >= 0;loop--){
  15. if(test[i+ loop] == test[j- loop])
  16. continue;
  17. else
  18. break;
  19. }
  20. if(loop == -1){
  21. if(j-i+1 > max){
  22. max=j-i+1;
  23. }
  24.  
  25. }
  26.  
  27. }
  28.  
  29. }
  30. printf("%d\n",max);
  31. }
  32.  
  33. int main()
  34. {
  35. int count=1;
  36. while(true)
  37. {
  38. string test;
  39. cin>>test;
  40. if(test == "END")
  41. return 1;
  42. else
  43. cout<<"Case "<<count++<<":";
  44. result(test);
  45.  
  46. }
  47. return 0;
  48. }

这种方法不难理解,对于一个字符串

记录其开始节点start,结尾节点end。以start为轴,不断去找使其loop为-1,即是回文的end。依次试,start++。记录最大值即可。

这种算法,因为对字符串循环遍历了两遍,所以时间复杂度O(n平方)。

第二种方法,动态规划:

  1. #include <cstdio>
  2. #include <iostream>
  3. #include <string>
  4. using namespace std;
  5.  
  6. void result (string test)
  7. {
  8. bool testarr[1000][1000] = {false};
  9. int count = test.size(),max = 0;
  10.  
  11. for(int i=0;i<count;i++){
  12.  
  13. testarr[i][i]=true;
  14.  
  15. if(i+1<count&&test[i]==test[i+1]){
  16.  
  17. max = 2;
  18. testarr[i][i+1] = true;
  19.  
  20. }
  21. }
  22. for(int len=3;len<=count;len++)
  23.  
  24. for(int i=0;i<=count-len;i++){
  25.  
  26. int j=i+len-1;
  27. if(testarr[i+1][j-1]&&test[i]==test[j]){
  28.  
  29. max=len;
  30. testarr[i][j]=true;
  31. }
  32.  
  33. }
  34. cout<<max<<endl;
  35. }
  36.  
  37. int main()
  38. {
  39. int count=1;
  40. while(true)
  41. {
  42. string test;
  43. cin>>test;
  44. if(test == "END")
  45. return 1;
  46. else
  47. cout<<"Case "<<count++<<":";
  48. result(test);
  49.  
  50. }
  51. return 0;
  52. }

一直以来其实都不是很理解动态规划的具体含义,只知道就那么回事。看动态规划的代码总是一看就懂,但具体使用,除非告诉我这道题是动态规划的题,否则就想不到。举一反三的能力一次也没拥有过。。。以后要加强。

这里的testarr[i][j]如果是true的话,说明这个字符串从i到j是回文,即其长度j-i+1。这个题目动态规划的思想在于,如果我已知testarr[i-1][j-1]是true,那么再判断test[i]与test[j]是否相等,如果相等,即把testarr[i][j]标记为true。

有意思的是,这里的外层循环不是遍历字符串,而是查找对应长度的回文,从长度为3开始,看字符串中是否含有长度为3的回文,内层循环从字符串的位置0开始查找。一次找看看最终能不能找到长度为size的回文,找到其中最大值,记录下来。

我把动态规划思想理解为一个由小变大,滚雪球的过程。在过程中产生一些结果,后面的结果又以前面的结果为基础,得到最终答案。

代码有的时候会有这种感受,写完之后再理思路感觉都没什么亮点,但在写的过程中,遇到的bug也真是煎熬。

第三种方法,Manacher方法:

  1. #include <cstdio>
  2. #include <string>
  3. #include <iostream>
  4. #include <algorithm>
  5. using namespace std;
  6.  
  7. char str[1000005];
  8. int p[1000005<<1];
  9. char a[1000005<<1];
  10. int min(int a,int b){
  11. return a>b?b:a;
  12. }
  13. void result(){
  14. int maxLine=0,ID=1,maxResult=0;
  15. int n=0,i,len,lentmp;
  16. lentmp=strlen(str);
  17. len=lentmp<<1;
  18.  
  19. for(i=0;i<(1000005<<1);i++){
  20. p[i]=0;
  21. a[i]='#';
  22. }
  23. for(i=0;i<lentmp;i++){
  24. a[((i+1)<<1)-1]=str[i];
  25.  
  26. }
  27.  
  28. for(i=1;i<len;i++){
  29.  
  30. if(maxLine >i){
  31. p[i]=min(p[2*ID-i],maxLine-i);
  32. }
  33. else{
  34. p[i]=1;
  35. }
  36.  
  37. while(a[i+p[i]]==a[i-p[i]]){
  38. p[i]++;
  39.  
  40. }
  41. if(p[i]+i>maxLine){
  42. maxLine=p[i]+i;
  43. ID=i;
  44. }
  45. if(p[i]>maxResult){
  46. maxResult=p[i];
  47. }
  48. }
  49.  
  50. cout<<maxResult-1<<endl;
  51. }
  52. int main()
  53. {
  54. int count=1;
  55. while(true)
  56. {
  57. cin>>str;
  58. if(strcmp(str,"END")==0)
  59. return 1;
  60. else
  61. cout<<"Case "<<count++<<": ";
  62. result();
  63. }
  64. return 0;
  65. }

刚刚介绍的方法都有弊端,前两种方法的弊端在于我遍历一遍字符串只对一个节点记录信息,就是所有我记录的信息都只对一个节点有用,这就导致我不得不对每一个节点都再遍历一遍,导致时间复杂度n的平方。第三种方法的弊端在于我已经知道字符串最大的回文子串撑破天可能也就是其size,那我就从2开始试呗。

而实际上,我可以试试在遍历一次字符串时得到信息的最大值。

这里变量的含义:

P[i]代表以i为中心时能够到达最远的字符。

maxLine实际上就代表已经扫描到的最右边的字符,即maxLine=P[ID]+ID。

ID代表当前使得扫描到最右边字符的那个位置上的字符。

maxResult 就是记录最终的结果值,即找到最大的那个P[i]。

所以,到这里,整个算法最难理解的实际就是这么一段代码:

if(maxLine>i)

{

p[i]=min(p[2*ID-i],maxLine-i);

}

Else

{

p[i]=1;

}

注意,这里并非直接给P[i]盖棺定论,而只是给P[i]赋一个初始值,后面还要有判断,P[i]是否++。这个赋初始值其实也是整个算法的亮点。总结一下就是csdn上的一篇博客的评论挺经典的:

大家可以想象一下,如果P[2*ID-i]这个值很大很大。由于ID的对称性,阻止P[i]初始值的更大的,恰恰就是maxLine-i,因为更远的还没有比对,不知道结果。

而如果maxLine-i这个值很大很大,就是多远的都已经比对完了。那么也因为ID的对称性,P[i]初始值也即P[2*ID-i]。

自己总结的Manacher算法就这么多,我还很菜,还在努力进步,如果有不对的地方,小弟还望各路大牛指点指正。

版权声明:本文为博主原创文章,未经博主允许不得转载。

HihoCoder第一周与POJ3974:最长回文字串的更多相关文章

  1. hihocoder 第一周 最长回文字串

    题目1 : 最长回文子串 时间限制:1000ms 单点时限:1000ms 内存限制:64MB 描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程 ...

  2. POJ 3974 最长回文字串(manacher算法)

    题意:给出一个字符串,求出最长回文字串. 思路:一开始我直接上了后缀数组DC3的解法,然后MLE了.看了DISCUSS发现还有一种计算回文字串更加优越的算法,就是manacher算法.就去学习了一下, ...

  3. 最长回文字串——manacher算法

    时间复杂度:O(n) 参考:https://segmentfault.com/a/1190000003914228 1.问题定义 最长回文子串问题:给定一个字符串,求它的最长回文子串长度. 如果一个字 ...

  4. 求字符串的最长回文字串 O(n)

    昨天参加了某公司的校园招聘的笔试题,做得惨不忍睹,其中就有这么一道算法设计题:求一个字符串的最长回文字串.我在ACM校队选拔赛上遇到过这道题,当时用的后缀数组AC的,但是模板忘了没写出代码来. 回头我 ...

  5. Hdu 3068 最长回文字串Manacher算法

    题目链接 最长回文 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total S ...

  6. 【POJ3974】最长回文字串

    在这里采用的是哈希+二分的方法. 根据回文串的性质可知,可以将回文分成奇回文和偶回文分别进行处理. 对于奇回文来说,每次枚举的端点一定是重合的,因此只需计算出端点左右公共的长度是多少即可,因此二分的是 ...

  7. 最长回文字串 (The longest palindrome substring)

    这两天去学了一下,觉得下面那篇文章写的很好,有例子,比较容易懂,所以转一下. 以下内容来自:hihoCoder: 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互 ...

  8. 【LeetCode每天一题】Longest Palindromic Substring(最长回文字串)

    Given a string s, find the longest palindromic substring in s. You may assume that the maximum lengt ...

  9. Leetcode5.Longest Palindromic Substring最长回文字串

    给定一个字符串 s,找到 s 中最长的回文子串.你可以假设 s 的最大长度为1000. 示例 1: 输入: "babad" 输出: "bab" 注意: &quo ...

随机推荐

  1. 第三节:Vuejs常用特性2和图书案例

    一. 常用特性2 1. 监听器 用watch来响应数据的变化, 一般用于异步或者开销较大的操作, watch 中的属性 一定是data 中 已经存在的数据!!! 当需要监听一个对象的改变时,普通的wa ...

  2. UINavigationController+FDFullscreenPopGesture全屏回滑手势阅读理解

    滑动返回纯oc.纯swifthttps://github.com/Bonway/BBGestureBack UINavigationController+FDFullscreenPopGesture全 ...

  3. 「模板」可持久化 HFQ-Treap

    老师用的是静态数组的写法,开了很多数组- 其实个人更倾向于 struct 或者用 class 封装起来. 但是鉴于太难打 好吧,是我懒得打. 然后就借鉴了老师的模板,写出了属于自己的 压行 风格. 代 ...

  4. 一个linuxk开发板的开发笔记

    arm-fsl-linux-gnueabi开发笔记 //开发主机系统信息 $ lsb_release -a No LSB modules are available. Distributor ID:U ...

  5. 九 AOP的概述

    AOP : 面向切面编程,解决OOP(面向对象编程)开发遇到的问题,是oop的延伸和扩展 AOP的优点:不修改源码的情况下,对程序进行校验,日志记录,性能控制,事务控制 SpringAOP底层的实现原 ...

  6. 02-08Android学习进度报告八

    今天主要学习了昨天还没有学习完的Date & Time组件的知识. 首先是DatePicker(日期选择器) android:calendarTextColor : 日历列表的文本的颜色 an ...

  7. 页面渲染时js阻塞的解决方法

    一般地,一个包含外部样式表文件和外部脚本文件的HTML载入和渲染过程是这样的: 浏览器下载HTML文件并开始解析DOM. 遇到样式表文件link[rel=stylesheet]时,将其加入资源文件下载 ...

  8. js加密(九)hr.bibibi md5

    1. 寻找加密js: 2. 结果: 3. execjs调用js即可.

  9. Flask - 数据库相关

    1. Flask-SQLAlchemy 1.1 参考: http://flask-sqlalchemy.pocoo.org/2.3/ https://github.com/janetat/flasky ...

  10. centos7搭建svn服务器及客户端设置

    centos7搭建svn服务器及客户端设置 centos7貌似预装了svn服务(有待确认),因此我们直接启动该服务即可 一.svn服务端配置(服务器IP假设为192.168.100.1) 步骤1:创建 ...