好久没有刷题了,虽然参加过ACM,但是始终没有融会贯通,没有学个彻底。我干啥都是半吊子,一瓶子不满半瓶子晃荡。
就连简单的Manacher算法我也没有刷过,常常为岁月蹉跎而感到后悔。

问题描述

给定一个字符串s,求最长回文子串。
回文子串的回文指的是abccba这种从前往后读和从后往前读一样。
子串必须连续(比如从i到j,s[i:j]),不是最长子序列(最长回文子序列怎么求?),子序列是可以不连续的。

算法大意

ans[i]表示以字符i为中心的最长回文子串的长度
now表示now+ans[now]取得最大值的那个下标
对于当前字符i,如果i处在以now为中心的回文子串里,那么ans[i]的求法可以参考i关于now的对称点的回文子串长度,也就是ans[now-(i-now)].
例如:1j34now67i9,假设ans[j]=1,那么ans[i]也等于1,因为i和j都处在以now为中心的回文子串里面,它们是对称的。

上面所述即为算法关键,其余情形很容易自己想到。
但是Manacher算法用到了两个技巧

加#号,统一处理

ans[i]中记录的是以i为中心的最长回文子串,如果不作处理,这样只能够检测出长度为奇数的回文子串的最大长度。所以有一个巧妙的预处理。
给定字符串abcc,扩充成#a#b#c#c#。
#a#b#a# 长度为3,以字符为中心的情况
#a#a#a#a# 长度为4,以#为中心的情况
这样奇数偶数统一化处理。

首部加上一个怪异字符$,减少条件判断

如果在for循环中检测两个条件,那是很费事的,效率低。
如何判断一个条件有很多次无效的判断?就看这个条件发挥作用,影响程序分支的次数和进行条件求值的次数。
边界条件判断影响分支的次数很少,但却每次都要进行判断。
通过加上一个终止字符,就能够避免边界条件判断。
在Manacher算法中,要求回文子串同时要防止下标越界。所以直接在开头插入一个\$字符,这样肯定因为失配而终止。

复杂度分析

Manacher算法为线性复杂度,因为从前往后有一个指针一直是单方向运动,没有回溯。
对于数组中的多个指针,如果都是单向运动,尽管它们运动的顺序和步长不同,那也一定是线性复杂度。

代码

  1. #include<stdio.h>
  2. #include<iostream>
  3. using namespace std;
  4. const int N = 110009;
  5. char s[N];
  6. char a[N * 2];
  7. int ans[N * 2];
  8. int now;
  9. int main(){
  10. freopen("in.txt", "r", stdin);
  11. while (scanf("%s", s) != -1){
  12. if (s[0] == 0)continue;
  13. //#号法预处理
  14. int j = 0;
  15. a[j++] = '$';//这样就能少判断一点,不用考虑边界问题了
  16. for (int i = 0; s[i]; i++){
  17. a[j++] = '#';
  18. a[j++] = s[i];
  19. }
  20. a[j++] = '#';
  21. //开始算法主体部分
  22. now = 1;
  23. ans[0] = ans[1] = 0;
  24. for (int i = 2; i < j; i++){
  25. if (now + ans[now] < i){//如果当前字符不在阴影里,只能自力更生
  26. int k = i;
  27. while (a[k] == a[i - (k - i)]) k++;
  28. ans[i] = k - i - 1;
  29. now = i;
  30. }
  31. else{
  32. int right = now - (i - now);
  33. if (right - ans[right]>now - ans[now]){
  34. ans[i] = ans[right];
  35. }
  36. else{
  37. int k = now + ans[now];
  38. while (a[k] == a[i - (k - i)])k++;
  39. ans[i] = k - i - 1;
  40. now = i;
  41. }
  42. }
  43. }
  44. //寻找答案,这部分可以直接放在求ans的过程中
  45. int ma = 0;
  46. for (int i = 1; i < j; i++){
  47. if (ma < ans[i])ma = ans[i];
  48. }
  49. printf("%d\n", ma);
  50. }
  51. return 0;
  52. }

最长回文子序列

动态规划:复杂度都是O(n^2)
方法一:
a[i,j]表示s[i,j]之间最长回文子序列。则a[i,j]可以来自a[i+1,j-1],a[i-1,j],a[i,j-1].
方法二:
将s和s反过来得到的字符串求最长公共子序列

HDU3068 回文串 Manacher算法的更多相关文章

  1. 最长回文---hdu3068 (回文串 manacher 算法模板)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3068 题意很清楚:就是求一个串s的子串中最长回文串的长度:这类题用到了manacher算法 #incl ...

  2. luoguP4555 [国家集训队]最长双回文串 manacher算法

    不算很难的一道题吧.... 很容易想到枚举断点,之后需要处理出以$i$为开头的最长回文串的长度和以$i$为结尾的最长回文串的长度 分别记为$L[i]$和$R[i]$ 由于求$R[i]$相当于把$L[i ...

  3. bzoj 2565: 最长双回文串 manacher算法

    2565: 最长双回文串 Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem. ...

  4. 37:密码截取(回文串manacher算法)

    题目描述:Catcher是MCA国的情报员,他工作时发现敌国会用一些对称的密码进行通信,比如像这些ABBA,ABA,A,123321,但是他们有时会在开始或结束时加入一些无关的字符以防止别国破解.比如 ...

  5. 【BZOJ2565】最长双回文串 (Manacher算法)

    题目: BZOJ2565 分析: 首先看到回文串,肯定能想到Manacher算法.下文中字符串\(s\)是输入的字符串\(str\)在Manacher算法中添加了字符'#'后的字符串 (构造方式如下) ...

  6. UESTC-1975弗吉桑(回文串,manacher算法)

    弗吉桑 Time Limit: 3000 MS     Memory Limit: 64 MB Submit Status 弗吉桑是一座横跨清水河大草原的活火山,位于子科技大学主楼东北方约 80km ...

  7. 回文串--Manacher算法(模板)

    用途:在O(n)时间内,求出以每一个点为中心的回文串长度. 首先,有一个非常巧妙的转化.由于回文串长度有可能为奇数也有可能为偶数,说明回文中心不一定在一个字符上.所以要将字符串做如下处理:在每两个字母 ...

  8. Palindrome(最长回文串manacher算法)O(n)

     Palindrome Time Limit:15000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit ...

  9. 九度OJ 1528 最长回文子串 -- Manacher算法

    题目地址:http://ac.jobdu.com/problem.php?pid=1528 题目描述: 回文串就是一个正读和反读都一样的字符串,比如"level"或者"n ...

随机推荐

  1. ASP.NET Core Kestrel部署HTTPS

    ASP.NET Core配置 Kestrel部署HTTPS.现在大部分网站已经部署HTTPS,大家对于安全越来越重视. 今天简单介绍一下ASP.NET Core 部署HTTPS,直接通过配置Kestr ...

  2. jQuery版AJAX简易封装

    开发过程中,AJAX的应用应该说非常频繁,当然,jQuery的AJAX函数已经非常好用,但是小编还是稍微整理下,方便不同需求下,可以简化输入参数,下面是实例代码: $(function(){ /** ...

  3. python之最强王者(4)——字符串

    1.Python 中文编码 前面章节中我们已经学会了如何用 Python 输出 "Hello, World!",英文没有问题,但是如果你输出中文字符"你好,世界" ...

  4. Java微信公众平台接口封装源码分享

    前言:      这篇博客是在三月初动手项目的时候准备写的,但是为了完成项目只好拖延时间写这篇博客,顺便也可以在项目中应用我自己总结的的一些经验.今天看来,这些方法的应用还是可以的,至少实现了我之前的 ...

  5. 应用.Net+Consul维护RabbitMq的高可用性

    懒人学习的过程就是工作中老大让干啥让做啥就研究研究啥,国庆放假回来的周末老大通过钉钉给我布置了个任务, RabbitMQ高可用解决方案,我想说钉钉太坑了: 这是国庆过后9号周日晚上下班给的任务,我周一 ...

  6. LinQ to SQL用法详解

    LinQ是指集成化查询语言,通过映射将数据库内的表名变为C#的类名,将列名作为属性名,将表的关系作为类的成员对象.O--M--R O-Object对象(李昌辉)R-Relation关系M-Mappin ...

  7. js对象和继承总结

    创建对象方式: [工厂模式]:无法解决对象识别问题 [构造函数模式]:每个方法都要在每个实例上创建一遍 [原型模式]:原型上属性为引用类型的问题,见例子 [组合模式]:解决上述问题 [动态原型模式]: ...

  8. android图片验证码--自绘控件

    自绘控件的内容都是自己绘制出来的 大致流程如下: 1.定义一个类继承view 使用TypedArray初始化属性集合 在view的构造方法中 有一个AttributeSet的参数 很明显是用来保存控件 ...

  9. 在 CentOS7 上将自定义的 jar 包注册为 linux 服务 service

    在 CentOS7 上将自定义的 jar 包注册为 linux 服务 service 1.在 /etc/rc.d/init.d/ 目录下创建一个名字和服务名完全相同的 shell 脚本文件 joyup ...

  10. [Modern OpenGL系列(一)]十步搞定OpenGL开发环境

    本文已同步发表在CSDN:http://blog.csdn.net/wenxin2011/article/details/51292143 OpenGL官网:https://www.opengl.or ...