原文转载自:http://blog.csdn.net/yzl_rex/article/details/7908259

回文串定义:“回文串”是一个正读和反读都一样的字符串,比如“level”或者“noon”等等就是回文串。回文子串,顾名思义,即字符串中满足回文性质的子串。

经常有一些题目围绕回文子串进行讨论,比如POJ3974最长回文,求最长回文子串的长度。朴素算法是依次以每一个字符为中心向两侧进行扩展,显然这个复杂度是O(N^2)的,关于字符串的题目常用的算法有KMP、后缀数组、AC 自动机,这道题目利用扩展KMP可以解答,其时间复杂度也很快O(N*logN)。

但是,今天笔者介绍一个专门针对回文子串的算法,其时间复杂度为O(n),这就是manacher 算法

大家都知道,求回文串时需要判断其奇偶性,也就是求aba 和abba 的算法略有差距。然而,这个算法做了一个简单的处理,很巧妙地把奇数长度回文串与偶数长度回文串统一考虑,也就是在每个相邻的字符之间插入一个分隔符,串的首尾也要加,当然这个分隔符不能再原串中出现,一般可以用‘#’或者‘$’等字符。例如:

原串:abaab

新串:#a#b#a#a#b#

这样一来,原来的奇数长度回文串还是奇数长度,偶数长度的也变成以‘#’为中心奇数回文串了

算法的中心思想

用一个辅助数组P 记录以每个字符为中心的最长回文半径,也就是P[i]记录以Str[i]字符为中心的最长回文串半径。P[i]最小为1,此时回文串为Str[i]本身。

我们可以对上述例子写出其P 数组,如下

新串: # a # b # a # a # b #

P[] : 1 2 1 4 1 2 5 2 1 2 1

我们可以证明P[i]-1 就是以Str[i]为中心的回文串在原串当中的长度。

证明:

1、显然L=2*P[i]-1 即为新串中以Str[i]为中心最长回文串长度。

2、以Str[i]为中心的回文串一定是以#开头和结尾的,例如“#b#b#”或“#b#a#b#”所以L 减去最前或者最后的‘#’字符就是原串中长度 的二倍,即原串长度为(L-1)/2,化简的P[i]-1。得证。

依次从前往后求得P 数组就可以了,这里用到了DP(动态规划)的思想, 也就是求P[i] 的时候,前面的P[]值已经得到了,我们利用回文串的特殊性质可以进行一个大大的优化。

先把核心代码贴上:

  1. for (i = 0; i < len; i++){
  2. if (maxid > i){
  3. p[i] = min(p[2*id - i], maxid - i);
  4. }
  5. else{
  6. p[i] = 1;
  7. }
  8. while (newstr[i+p[i]] == newstr[i-p[i]])
  9. p[i]++;
  10. if (p[i] + i > maxid){
  11. maxid = p[i] + i;
  12. id = i;
  13. }
  14. if (ans < p[i])
  15. ans = p[i];
  16. }

为了防止求P[i]向两边扩展时可能数组越界,我们需要在数组最前面和最后面加一个特殊字符,令P[0]=‘$’最后位置默认为‘\0’不需要特殊处理。此外,我们用MaxId 变量记录在求i 之前的回文串中,延伸至最右端的位置,同时用id 记录取这个MaxId 的id 值。通过下面这句话,算法避免了很多没必要的重复匹配。


  1. if (maxid > i){
  2. p[i] = min(p[2*id - i], maxid - i);
  3. }

那么这句话是怎么得来的呢,其实就是利用了回文串的对称性,如下图,

j=2*id-1 即为i 关于id 的对称点,根据对称性,P[j]的回文串也是可以对称到i 这边的,但是如果P[j]的回文串对称过来以后超过MaxId 的话,超出部分就不能对称过来了,如下图,

所以这里P[i]为的下限为两者中的较小者,p[i]=Min(p[2*id-i],MaxId-i)。算法的有效比较次数为MaxId 次,所以说这个算法的时间复杂度为O(n)。

下面就贴一个具体代码,求解最长回文字符串的代码:

  1. #include <iostream>
  2. #include <algorithm>
  3. #include <string>
  4. using namespace std;
  5. const int MAX = 100001;
  6. int len, p[2*MAX];
  7. char str[2*MAX], newstr[2*MAX];
  8. void change()
  9. {
  10. int i;
  11. newstr[0] = '@';
  12. newstr[1] = '#';
  13. for (i = 0; i < len; i++){
  14. newstr[2*i + 2] = str[i];
  15. newstr[2*i + 3] = '#';
  16. }
  17. newstr[2*len + 2] = '\0';
  18. return ;
  19. }
  20. void Manacher()
  21. {
  22. int i, j, id, maxid = 0, ans = 1;
  23. len = 2 * len + 2;
  24. for (i = 0; i < len; i++){
  25. if (maxid > i){
  26. p[i] = min(p[2*id - i], maxid - i);
  27. }
  28. else{
  29. p[i] = 1;
  30. }
  31. while (newstr[i+p[i]] == newstr[i-p[i]])
  32. p[i]++;
  33. if (p[i] + i > maxid){
  34. maxid = p[i] + i;
  35. id = i;
  36. }
  37. if (ans < p[i])
  38. ans = p[i];
  39. }
  40. for (i = id, j = 0; i < id + ans; i++){
  41. if (newstr[i] != '#'){
  42. str[j] = newstr[i];
  43. j++;
  44. }
  45. }
  46. str[id+ans] = '\0';
  47. cout << ans - 1 << " " << str << endl;
  48. return ;
  49. }
  50. int main()
  51. {
  52. while (scanf("%s", &str)){
  53. if (strcmp(str, "END") == 0) break;
  54. len = strlen(str);
  55. change();
  56. Manacher();
  57. }
  58. system("pause");
  59. }

Manacher算法:求解最长回文字符串,时间复杂度为O(N)的更多相关文章

  1. manacher算法求最长回文子序列

    一:背景 给定一个字符串,求出其最长回文子串.例如: s="abcd",最长回文长度为 1: s="ababa",最长回文长度为 5: s="abcc ...

  2. manacher算法求最长回文子串

    一:背景 给定一个字符串,求出其最长回文子串.例如: s="abcd",最长回文长度为 1: s="ababa",最长回文长度为 5: s="abcc ...

  3. Manacher算法 求 最长回文子串

    1 概述(扯淡) 在了解Manacher算法之前,我们得先知道什么是回文串和子串. 回文串,就是正着看反着看都一样的字符串.比如说"abba"就是一个回文串,"abbc& ...

  4. Manacher算法 - 求最长回文串的利器

    求最长回文串的利器 - Manacher算法 Manacher主要是用来求某个字符串的最长回文子串. 不要被manacher这个名字吓倒了,其实manacher算法很简单,也很容易理解,程序短,时间复 ...

  5. Manacher算法——求最长回文子串

    首先,得先了解什么是回文串.回文串就是正反读起来就是一样的,如“abcdcba”.我们要是直接采用暴力方法来查找最长回文子串,时间复杂度为O(n^3),好一点的方法是枚举每一个字符,比较较它左右距离相 ...

  6. manacher 算法(最长回文串)

    manacher算法: 定义数组p[i]表示以i为中心的(包含i这个字符)回文串半径长 将字符串s从前扫到后for(int i=0;i<strlen(s);++i)来计算p[i],则最大的p[i ...

  7. 使用manacher算法解决最长回文子串问题

    要解决的问题 求一个字符串最长回文子串是什么.且时间复杂度 O(N) 具体描述可参考: LeetCode_5_最长回文子串 LintCode_200_最长回文子串 暴力解法 以每个字符为中心向左右两边 ...

  8. hdu 3068 最长回文 (Manacher算法求最长回文串)

    参考博客:Manacher算法--O(n)回文子串算法 - xuanflyer - 博客频道 - CSDN.NET 从队友那里听来的一个算法,O(N)求得每个中心延伸的回文长度.这个算法好像比较偏门, ...

  9. leetcode 5 Longest Palindromic Substring(Manacher算法求最长回文串)

    应用一下manacher算法就可以O(n)求出结果了.可以参考hdu3068 substr(start,length)函数是这样用的: substr 方法 返回一个从指定位置开始,并具有指定长度的子字 ...

随机推荐

  1. Java中的阻塞队列-ArrayBlockingQueue(一)

    最近在看一些java基础的东西,看到了队列这章,打算对复习的一些知识点做一个笔记,也算是对自己思路的一个整理,本章先聊聊java中的阻塞队列 参考文章: http://ifeve.com/java-b ...

  2. qingdao

    1001 #include <bits/stdc++.h> using namespace std; vector<long long> v; long long pow2(l ...

  3. C++中的虚函数表

    (感谢http://blog.csdn.net/haoel/article/details/1948051/) C++中的虚函数的作用主要是实现了多态的机制. 多态,简而言之就是用父类型别的指针指向其 ...

  4. 在mac上使用github for mac 创建并上传项目

    1.下载github for mac https://mac.github.com/ 2.登陆 偏好设置 3.用Xcode 创建一个项目,勾上“create local git respository ...

  5. Python面向对象(三)

    一.绑定方法与非绑定方法 一.绑定方法:绑定给谁就应该由谁来调用,谁来调用就会将谁当作第一个参数传入 1.绑定给对象的方法:类中定义的函数默认就是绑定给对象的 2.绑定给类的方法:为类中定义的函数加上 ...

  6. 在CentOS的profile文件中配置环境变量

    # idea jdk7 settings start #JAVA_HOME=/usr/local/jdk/jdk1.7.0_79 #JRE_HOME=$JAVA_HOME/jre #PATH=$PAT ...

  7. js之正则表达式(RegExp对象)

    先看一个很有意思的例子: 用字面量的方式定义了一个正则表达式 /\w/g,再重复匹配字符串 ‘ab’ 的时候,出现了结果不唯一的现象. 很多新手都对这种现象感到困惑,难道是正则表达式不稳定吗? 接下来 ...

  8. 即将要被淘汰的兼容之--CSS Hack

    css hack 条件注释法只在IE下生效<!--[if IE]>这段文字只在IE浏览器显示<![endif]-->只在IE6下生效<!--[if IE 6]>这段 ...

  9. 转:解决Arcsde用户锁定的问题

    采用arcgis平台做GIS应用的人,可能偶尔碰到sde用户锁定(Arccatalog 或应用程序异常退出的时比较多)的问题,往往咱们解决的办法是重启sde服务.如果一个服务器上有多个连接时,重启服务 ...

  10. Xposed截获 Android手机QQ密码

    0x00 前言 Xposed框架是一款修改系统框架服务的软件,通过它许多功能强大的模块得以实现,且不冲突地同时运作,自从Xposed框架发布以来,安卓手机的可玩性日益激增,最近很闲很蛋疼,研究下截获A ...