求最长回文子串的算法比较经典的是manacher算法

转载自这里

首先,说明一下用到的数组和其他参数的含义:

(1)p[i] : 以字符串中下标为的字符为中心的回文子串半径长度;

例如:abaa字符串,那么p[1]=2,(以b为中心的回文子串是aba,半径长度为2。计算半径时包括b本身)
所以,数组的最大值就是最长回文串的半径。

(2)id
id 为当前已确定的边界能伸展到最靠右的回文串的中心。

例如:

  • 设黑色的线段表示字符串范围
  • 红色的线段表示当前已经确定的回文子串在主串中的范围

虽然左边的红色线段(左边的回文子串)长度比右边长,但是,记录的中心是右边红色线段的中心,因为右边的回文子串伸展更靠右。(下面将会解释为什么要记录这个id值)

manacher算法是从左到右扫描的,所以,在计算时,都是已知的。

manacher算法

假设现在扫描到主串下标为的字符,那么,以为中心的回文子串最可能和之前已经确定的哪个回文子串有交集? 没错,就是能伸展最靠右的回文子串,也就是以为中心的回文子串。至于这个交集有什么用,下面将解释。

以i为中心的回文串和以id为中心的回文串如果有交集,会出现三种情况:

(1)

其中,2*id-i为i以id为中心的对称位置。(2*id-i这个就不用多说了吧,计算一下就得到了)

第一种情况是(上图),以2id-i为中心的回文子串左端超出了以id为中心的回文子串(绿色部分左端超出黑色部分左端)。那么,根据回文串的特点,知道以2id-i为中心的两边橙色部分是对称的,同样,若以id为中心,这两段橙色部分又对称id右边两段橙色部分,所以,以i为中心的两段橙色部分也是回文串。

这种情况下, p[i]=p[id]+id-i (橙色部分的长度)

那么,以i为中心的橙色部分有没有可能更长?这是不可能的,假设还可以更长,如下:

a和b对称,b和c对称,c和d对称,最终得到a和d对称,那么,以id为中心的回文串长度就不是下面黑色部分的长度了,而应左端加a右端加d,与已经求得的长度矛盾。

所以这种情况下: p[i]=p[id]+id-i

(2)

第二种情况是以2*id-i为中心的回文子串在以id为中心的回文子串内,如上图。此时,p[i]=p[2*id-i] ,那么,以i为中心的绿色部分还可以伸展吗?假设可以,如下:

同样,c和d对称,b和c对称,a和d对称,得到a和b对称,那么以2*id-i为中心的回文子串长度就不是绿色部分的长度了,需要左端加a右端加b,与以求得的长度矛盾。

所以,这种情况下 p[i]=p[2*id-i]

(3)

第三种情况是,以2*id-i为中心的回文子串左端与以id为中心的回文子串的左端恰好重合。则有。也就是说在的基础上,还可能增加,即以i为中心的绿色部分还可能伸展。

所以,需要用一个while循环来确定它能增加多少。while循环为:

while (s[i - p[i]] == s[i + p[i]])++p[i];

也就是判断绿色两端(下图浅黑色线段)是否相同,如果相同,就可以不断增加。(理解p[i]的意思,就理解这个循环了)

如果没有出现交集(上面三种情况),那么就以i为中心点找最长回文子串(下面代码中else的情况)。所以,算法主要是利用回文串的交集来减少计算

如果我们将上面的情况总结起来,代码将非常简洁:

if (p[id] + id - 1 >= i)
//没有超出当前边界最右的回文串,也就是上面出现交集三种情况中的一种p[i] = Min(p[2 * id - i], p[id] + id - i);
else//如果没有交集,就以它为中心求回文串长度p[i] = 1;while (s[i - p[i]] == s[i + p[i]])++p[i];

最后,需要注意的是,上面的讨论都是以某个字符为中心的回文串,比如像这样的回文串:aabaa (长度为奇数)。但是,如果是这样的回文串:aabbaa(长度为偶数),就没法处理。

我们可以通过插入特殊符号(如‘#’)的办法,将字符串统一为奇数长度,如aabaa 变为 #a#a#b#a#a# ;同理,aabbaa变为#a#a#b#b#a#a#

注意到,上面的代码:

while (s[i - p[i]] == s[i + p[i]])++p[i];

可能越界(超过头或尾),我们可以通过头尾也加入不相同的特殊符号处理,如aabaa 变为 $ #a#a#b#a#a# @。这种办法为什么可行呢?我们举个例子,还是以aabaa为例,它变成 $ #a#a#b#a#a# @。当i指向第一个a时(也就是i=2),这时,s[i-1]==s[i+1];继续比较s[i-2]≠s[i+2](也就是比较$和a),就不会超过头。所以,就避免了越界的现象。末尾加个@也是同样的道理。

最长回文串的长度是maxlen-1
解释

  • p[i]为回文串的半径,如果该半径是以 # 开始,即 # s[i] #....# 则一定以 # 结束,所以 maxlen-1 ,恰好是 s[] 和 # 一样多,也就是maxlen-1是原串以 i 为中心的回文串的长度
  • 如果该半径是以s[]开头,即 ....# s[i-1] # s[i] # s[i+1] #.... ,显然回文串的长度是p[i]-1

下面是hihoCoder的一道求最长回文子串的题:http://hihocoder.com/problemset/problem/1032

#include<iostream>
#include<string>  int Min(int a, int b)
{if (a < b)return a;return b;
}int LPS(std::string &s)
{std::string new_s="";int s_len = s.length();new_s.resize(2 * s_len + 3);int new_s_len = new_s.length();new_s[0] = '$';new_s[1] = '#';new_s[new_s_len - 1] = '@';for (int i = 0,j=2; i < s_len; ++i){new_s[j++] = s[i];new_s[j++] = '#';}int *p = new int[new_s_len + 1];int id = 0;//记录已经查找过的边界最靠右回文串的中心int maxLPS = 1;p[0] = 1;for (int i = 1; i < new_s_len-1; ++i){if (p[id] + id - 1 >= i)//有交集的情况p[i] = Min(p[2 * id - i], p[id] + id - i);else//无交集的情况p[i] = 1;while (new_s[i - p[i]] == new_s[i + p[i]])//确定能伸展多少,上面if的情况是不会执行这个循环的++p[i];if (p[id] + id < p[i] + i)//重新确定伸展最右的回文子串中心id = i;if (p[i]>maxLPS)//保存当前最长回文子串的长度(还要-1)maxLPS = p[i];}delete[] p;return maxLPS - 1;
}int main()
{int N;std::string s;std::cin >> N;while (N--){std::cin >> s;std::cout<<LPS(s)<<std::endl;}return 0;
}

HDU3068 使用C语言字符数组来加快速度

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<iostream>
#include<math.h>
#include<queue>
#include<map>
#include<vector>
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
const int maxn=2e5+10;
char s[maxn],new_s[maxn*2];int LPS(){int s_len=strlen(s);int new_s_len=s_len*2+3;new_s[0]='$';new_s[1]='#';new_s[new_s_len-1]='@';int i,j;for(i=0,j=2;i<s_len;i++){new_s[j++]=s[i];new_s[j++]='#';}new_s[j]='\0';int *p=new int[new_s_len+1];int id=0;int maxLPS=1;p[0]=1;for(int i=1;i<new_s_len;i++){if(p[id]+id-1>=i)p[i]=min(p[2*id-i],p[id]+id-i);elsep[i]=1;while(new_s[i-p[i]]==new_s[i+p[i]])p[i]++;if(p[id]+id<p[i]+i)id=i;if(p[i]>maxLPS)maxLPS=p[i];}delete[] p;return maxLPS-1;
}
int main(){int ca=1;while(scanf("%s",s)!=EOF){memset(new_s,0,sizeof new_s);printf("%d\n",LPS());}return 0;
}

更加高效简单的写法

#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<iostream>
#include<math.h>
#include<queue>
#include<map>
#include<vector>
using namespace std;
#define inf 0x3f3f3f3f
#define ll long long
const int maxn=2e5+10;
char s[maxn<<1];
int p[maxn<<1];//p[i]是回文串的半径int LPS(){int len=strlen(s);int maxlen=1;//最长回文字符串的长度int id=0;for(int i=len;i>=0;i--){//插入len+1个”#“s[i+i+2]=s[i];s[i+i+1]='#';}//最终的S的长度是1~2*len+1s[0]='*';//防止while时p[i]越界for(int i=2;i<2*len+1;i++){if(p[id]+id>i)p[i]=min(p[2*id-i],p[id]+id-i);elsep[i]=1;while(s[i-p[i]]==s[i+p[i]])p[i]++;if(id+p[id]<i+p[i])id=i;if(maxlen<p[i])maxlen=p[i];}return maxlen-1;
}
int main(){while(scanf("%s",s)!=EOF){printf("%d\n",LPS());}return 0;
}

HihoCode1032 最长回文子串 manacher算法相关推荐

  1. lintcode最长回文子串(Manacher算法)

    题目来自lintcode, 链接:http://www.lintcode.com/zh-cn/problem/longest-palindromic-substring/ 最长回文子串 给出一个字符串 ...

  2. 求解最长回文子串----Manacher 算法

    最长回文子串问题:给定一个字符串,求它的最长回文子串长度. 如果一个字符串正着读和反着读是一样的,那么我们称之为回文串.例如:abba.aaaa.abvcba.123321等 暴力法:遍历字符串的所有 ...

  3. 最长回文子串——Manacher 算法​​​​​​​

    0. 问题定义 最长回文子串问题:给定一个字符串,求它的最长回文子串长度. 如果一个字符串正着读和反着读是一样的,那它就是回文串.下面是一些回文串的实例: 12321 a aba abba aaaa ...

  4. 最长回文子串manacher算法模板

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

  5. 【字符串】最长回文子串 ( 蛮力算法 )

    文章目录 一.回文串.子串.子序列 二.最长回文子串 1.蛮力算法 2.时间复杂度最优方案 一.回文串.子串.子序列 " 回文串 ( Palindrome ) " 是 正反都一样的 ...

  6. 回文字符串—回文子串—Manacher算法

    leetcode地址:5. 最长回文子串 解答参考:动态规划.中心扩散.Manacher 算法 问题描述: 给你一个字符串 s,找到 s 中最长的回文子串.比如给定字符串s = "babad ...

  7. manacher java_最大回文子串(Manacher算法)

    1.Manacher算法 首先说明一下,Manacher算法能够使得在O(n)的时间复杂度下找到最长的回文子串. (1).Manacher算法的概述 Manacher算法只能解决长度为奇数的字符串,所 ...

  8. 最长回文子串 -- 马拉松算法

    百度了好长时间,看了很多篇博客才稍微看懂,所以自己写篇博客加深一下映像,并且写的尽量详细一些 希望大家能够只这篇博客就能看懂,能少走些弯路 马拉松算法 1.添加特殊字符 通常情况下,对于一个字符串,需 ...

  9. 【字符串】最长回文子串 ( 动态规划算法 ) ★

    文章目录 一.回文串.子串.子序列 二.最长回文子串 1.动态规划算法 2.动态规划算法代码示例 一.回文串.子串.子序列 " 回文串 ( Palindrome ) " 是 正反都 ...

最新文章

  1. C语言-文件操作总结
  2. youtube根据channelId抓取栏目
  3. odoo13中的模型类中的方法函数ORM方法以及模型方法的装饰器
  4. 三态门三个状态vhdl_人防门是什么?为什么会侵线导致重庆地铁事故
  5. zookeeper 默认端口_ZooKeeper知识点汇总
  6. php伪静态之APACHE配置篇
  7. B-Tree 索引和 Hash 索引的对照
  8. 电气simulink常用模块_重庆台达PLC控制柜施工技术标准 - 重庆电工电气
  9. 《信号与系统》(吴京)部分课后习题答案与解析——第五章(PART3)(系统)
  10. 软件人员绩效考核新思路
  11. OPENWRT 修改串口(ttyS*)笔记
  12. 脱机运行scp linux,解决CentOS使用不了scp命令
  13. 城市云脑,像大脑一样建设智慧城市,基于互联网云脑的新架构
  14. 跟我学spring security系列文章第一章 实现一个基本的登入
  15. 2020年2月中国编程语言排行榜
  16. #*nginx: [warn] conflicting server name “localhost“ on 0.0.0.0:80, ignored
  17. PTA 多项式A除以B (25 分)
  18. mysql和sql server连接不上_php-通过不可靠的网络连接在MySQL和SQL Server...
  19. 华为java校招面试流程_华为面试流程是什么?
  20. SAP MDG —— 数据建模 Data Modeling

热门文章

  1. 实现一个简单的代码字计数器(二)
  2. boost--线程同步
  3. x-code 4.61 无证书真机调试
  4. ASP.NET技术的学习顺序
  5. python函数调用追踪_Python函数调用追踪实现代码
  6. java可变长字符串_具有可变长度的scala字符串插值
  7. 算法c语言源码_C语言实现推箱子
  8. c#图片base64去转义字符_C#实现字符串与图片的Base64编码转换操作示例
  9. linux中rev命令详解,详解Linux中输出文件内容的rev与tac命令使用
  10. Spring添加对缓存的支持