参考博客:

  • https://blog.csdn.net/qq_43456058/article/details/9458872
  • https://blog.csdn.net/u011469138/article/details/82431327
  • https://blog.csdn.net/qq_40620465/article/details/90183663?utm_source=app
  • https://www.cnblogs.com/grandyang/p/4475985.html

马拉车算法:

  • 什么是马拉车?
    Manacher算法,又叫“马拉车”,它可以在时间复杂度和空间复杂度都是O(n)的情况下,求出一个字符串的最长回文串长度(一般用法,也可以求字符串中回文串的个数)

  • 第一步,重组字符串
    例如: noon
    重组后:$#n#o#o#n#
    那为什么要这样做呢?
    1.首先,这样做的好处是不论原字符串是奇数还是偶数个,处理之后得到的字符串的个数都是奇数个,这样就不用分情况讨论了,而可以一起搞定。
    2.接下来我们还需要和处理后的字符串t等长的数组p,其中 p[i] 表示以 t[i] 字符为中心的回文子串的半径,若 p[i] = 1,则该回文子串就是 t[i] 本身,那么我们来看一个简单的例子:

    # 1 # 2 # 2 # 1 # 2 # 2 #
    1 2 1 2 5 2 1 6 1 2 3 2 1
    

    为啥我们关心回文子串的半径呢?看上面那个例子,以中间的 ‘1’ 为中心的回文子串 “#2#2#1#2#2#” 的半径是6,而未添加#号的回文子串为 “22122”,长度是5,为半径减1。这是个普遍的规律么?我们再看看之前的那个 “#b#o#b#”,我们很容易看出来以中间的 ‘o’ 为中心的回文串的半径是4,而 "bob"的长度是3,符合规律。再来看偶数个的情况 “noon”,添加#号后的回文串为 “#n#o#o#n#”,以最中间的 ‘#’ 为中心的回文串的半径是5,而 “noon” 的长度是4,完美符合规律。所以我们只要找到了最大的半径,就知道最长的回文子串的字符个数了。只知道长度无法定位子串,我们还需要知道子串的起始位置。
    我们还是先来看中间的 ‘1’ 在字符串 “#1#2#2#1#2#2#” 中的位置是7,而半径是6,貌似 7-6=1,刚好就是回文子串 “22122” 在原串 “122122” 中的起始位置1。那么我们再来验证下 “bob”,“o” 在 “#b#o#b#” 中的位置是3,但是半径是4,这一减成负的了,肯定不对。所以我们应该至少把中心位置向后移动一位,才能为0啊,那么我们就需要在前面增加一个字符,这个字符不能是#号,也不能是s中可能出现的字符,所以我们暂且就用美元号$吧,毕竟是博主最爱的东西嘛。这样都不相同的话就不会改变p值了,那么末尾要不要对应的也添加呢,其实不用的,不用加的原因是字符串的结尾标识为 ‘\0’,等于默认加过了。那此时 “o” 在 “KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲b#o#b#" 中的位置是4,…#1#2#2#1#2#2#” 中的位置是8,而半径是6,这一减就是2了,而我们需要的是1,所以我们要除以2。之前的 “bob” 因为相减已经是0了,除以2还是0,没有问题。再来验证一下 “noon”,中间的 ‘#’ 在字符串 “$#n#o#o#n#” 中的位置是5,半径也是5,相减并除以2还是0,完美。可以任意试试其他的例子,都是符合这个规律的,最长子串的长度是半径减1,起始位置是中间位置减去半径再除以2。(此处偷了懒,复制了其他博客!如有侵权,请联系删除!抱歉!)

  • 第二步,理解下面这段代码

    // id 代表中心位置, mx 代表最右边的位置
    p[i] = mx > i ? min(p[id * 2 - i],mx - i):1;
    

    看上去不是很好理解,下面开始分步讲解
    首先对数据进行初始化,再对i进行判断,分为两种情况
    第一种情况,i>=mx,i在mx前面,直接让p[i]=1。
    第二种情况,i<mx,这时候就又有两种情况了,对p[j]和mx-i进行比较:
    (1)p[j]<=mx-i说明i的最右端还在mx里面,如上图所示,只需要让p[i]=p[j]即可。
    (2)p[j]>mx说明i的最右端大于mx了,如下图所示,所以我们需要对这两种情况再讨论一下,当p[j] < mx-i的时候,表示Len[i]的长度可能不会超过mx-i,所以我们就从i的p[2*id - i]也就是p[mx-i]的地方开始匹配。当p[j] > mx - i的时候,说明i位置的子串长度超过了mx,但mx以外的地方还没有遍历到,所以我们就从mx-i也就是mx的位置开始对i匹配。
    如果p[i]+i>mx,就对mx进行更新,并且将中间点id更换成i,再通过比较更新最长回文串长度,返回最大值。

  • 马拉车完整代码(求最长回文子串的长度):

/*
马拉车模板题: https://www.luogu.org/problemnew/solution/P3805
*/
//求最长回文串的长度
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 32000005;
int hw[MAXN];
//马拉车
inline int Manacher(string s)
{//转换字符串memset(hw, 0, sizeof(hw));int len = s.length();string nowString = "$#";for(int i = 0; i < len; i++){nowString += s[i];nowString += "#";}//防止越界访问len = nowString.length();int maxRight = 0, mid = 0, maxAns = 0;//maxRight 最右边的位置 mid 中心的位置 maxAns 最长回文串的半径for(int i = 1; i < len; i++){if(maxRight > i){//当中心点没超过最右边maxRighthw[i] = min(maxRight - i, hw[(mid<<1) - i]);}else{//否则,就重新往外扩hw[i] = 1;}//如果当前不是最长, 就往外扩while(nowString[i + hw[i]] == nowString[i - hw[i]]){++hw[i];}//更新最右边的位置, 同时更新最长字串的半径if(i + hw[i] > maxRight){maxRight = i + hw[i];mid = i;}maxAns = max(hw[i], maxAns);}//最长字串的长度等于半径减1return (maxAns - 1);
}
int main()
{ios::sync_with_stdio(false);string s;while(cin >> s){cout << Manacher(s) << endl;}return 0;
}
  • 马拉车求最长回文串的个数
//求回文串的个数
inline int Manacher(string s)
{//转换字符串memset(hw, 0, sizeof(hw));int len = s.length();string nowString = "$#";for(int i = 0; i < len; i++){nowString += s[i];nowString += "#";}//防止越界访问nowString += "^";len = nowString.length();int maxRight = 0, mid = 0, maxAns = 0, numAns = 0;//maxRight 最右边的位置 mid 中心的位置 maxAns 最长回文串的半径for(int i = 1; i < len; i++){if(maxRight > i){//当中心点没超过最右边maxRighthw[i] = min(maxRight - i, hw[(mid<<1) - i]);}else{//否则,就重新往外扩hw[i] = 1;}//如果当前不是最长, 就往外扩while(nowString[i + hw[i]] == nowString[i - hw[i]]){++hw[i];}//更新最右边的位置, 同时更新最长字串的半径if(i + hw[i] > maxRight){maxRight = i + hw[i];mid = i;}//求最长回文串的长度//maxAns = max(hw[i], maxAns);//求回文串的个数numAns += (hw[i] / 2);}//最长字串的长度等于半径减1//return (maxAns - 1);//回文串的个数return numAns;
}

这里我们来探讨下为什么是:

//求回文串的个数
numAns += (hw[i] / 2);

例如:
#a#b#a#
1 2 1 4 1 2 1
这已经给出了每个字符所能延伸的半径, 例如b为4,因为这之内的所有字符都是以b字符为中心对称,又因为字符串中加入了#字符,所以以b字符为中心点的回文串个数为hw[b的下标] / 2

  • 求最长回文串
//求最长回文串
inline string Manacher(string s)
{//转换字符串memset(hw, 0, sizeof(hw));int len = s.length();string nowString = "$#";for(int i = 0; i < len; i++){nowString += s[i];nowString += "#";}//防止越界访问nowString += "^";len = nowString.length();int maxRight = 0, mid = 0, maxLen = 0, maxPoint = 0;//maxRight 最右边的位置 mid 中心的位置 maxAns 最长回文串的半径for(int i = 1; i < len; i++){if(maxRight > i){//当中心点没超过最右边maxRighthw[i] = min(maxRight - i, hw[(mid<<1) - i]);}else{//否则,就重新往外扩hw[i] = 1;}//如果当前不是最长, 就往外扩while(nowString[i + hw[i]] == nowString[i - hw[i]]){++hw[i];}//更新最右边的位置, 同时更新最长字串的半径if(i + hw[i] > maxRight){maxRight = i + hw[i];mid = i;}if(hw[i] > maxLen){maxLen = hw[i];maxPoint = i;   //最长回文串的中心位置}}//截取最长回文串//这里为啥这样写,在本博客前文已经提到过: 1. 第一步,重组字符串return s.substr((maxPoint - maxLen) / 2, maxLen - 1);
}

后续再补上一些例题,以及求字符串中所有的回文串个数,最长的回文串!

2019.07.26, 已经将基本的马拉车算法应用补上.

待补题: 杭电6599

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

马拉车算法(求最长回文串)相关推荐

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

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

  2. 【HDU - 3068】最长回文(Manacher算法,马拉车算法求最长回文子串)

    题干: 给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度.  回文就是正反读都是一样的字符串,如aba, abba等 Input 输入有多组case,不超过120组 ...

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

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

  4. Manacher's Algorithm 马拉车算法(最长回文串)

    这个马拉车算法Manacher's Algorithm是用来查找一个字符串的最长回文子串的线性方法,由一个叫Manacher的人在1975年发明的,这个方法的最大贡献是在于将时间复杂度提升到了线性,这 ...

  5. Manacher马拉车算法求最长回文子串

    终于把马拉车算法搞明白了!赶紧记录一下. 这个算法用于查找一个字符串的最长回文子串 马拉车算法依次给数组p[i]赋值,马拉车算法的本质就是在每次给数组p[i] 赋值时尝试进行偷懒 例如,当要给p[6] ...

  6. 最长回文 HDU - 3068(求最长回文串的长度【马拉车算法Manacher】)

    马拉车算法 Manacher's Algorithm 是用来查找一个字符串的最长回文子串的线性方法,由一个叫 Manacher 的人在 1975 年发明的,这个方法的最大贡献是在于将时间复杂度提升到了 ...

  7. 求最长回文串-从动态规划到马拉车之路(下)

    预备知识: (1)在一个数轴上有两点i和j(i<=j)关于点m对称,那么有 i = 2m-j: 证明: 因为 i<=j 且 i 和 j 关于 m 对称,那么有 (i + j)/ 2 = m ...

  8. 求最长回文串-从动态规划到马拉车之路(上)

    要解决的问题: 给定一个字符串,要求求出这个字符串中的最长的回文串子串. 例子: cbddba的最长回文子串为 bddb cbdedba的最长回文子串为dbedb 由上面的例子可以看到,在考虑回文子串 ...

  9. Manacher's algorithms(马拉车算法)最长回文子串

    最长回文子串 https://leetcode-cn.com/problems/longest-palindromic-substring/ 给定一个字符串 s,找到 s 中最长的回文子串.你可以假设 ...

  10. python求最大回文数_python最长回文串算法

    给定一个字符串,要求在这个字符串中找到符合回文性质的最长子串.所谓回文性是指诸如 "aba","ababa","abba"这类的字符串,当然 ...

最新文章

  1. DataGridView合并表头实现 、二维表头的实现
  2. c语言中判断一个字符串是否包含另一个字符串
  3. Nutanix公布财报数据 准备IPO前表现抢眼
  4. 函数重载二义性:error C2668: 'pow' : ambiguous call to overloaded function
  5. count函数里加函数_PHP count()函数与示例
  6. Dream------Hadoop--HDFS的设计
  7. java语言有哪些优点
  8. 如何使用EA画ER图?
  9. PCWorld:Google到底要干什么?
  10. 【转】收集各种反编译工具 常用EXE文件反编译工具下载
  11. PFC离散元软件快捷操作方式
  12. 高效办公软件推荐——屏幕录制类
  13. java中system.in怎么用
  14. 宝塔面板怎么实名认证_宝塔面板安装和使用图文教程
  15. uc3854 matlab仿真,基于UC3854硬开关PFC变换电路设计课程设计.doc
  16. 理解析取范式及合取范式的意义
  17. win10怎么录屏幕视频带声音?有哪些需要注意的地方?
  18. php限制pc访问,禁止直接访问网首首页index.php文件的方法Windows服务器操作系统 -电脑资料...
  19. 如何更改AutoCAD软件图纸背景颜色?
  20. 补丁问题(WannaCry)补丁问题

热门文章

  1. Mega网盘来下载外国友人分享的资源
  2. win10 电脑没声音 控制面板 realtek高清晰音频管理器没有解决方案
  3. low power-upf-vcsnlp(五)
  4. Windows10更新后,如何删除多出来的OEM分区?
  5. Netty权威指南2.2伪异步IO,Demo代码
  6. 颜色选择器(拾色器)
  7. 全国省份及城市按字母顺序进行排序js文件
  8. java swing个人简历小实验
  9. Windows系统连接Linux的常用网管工具下载
  10. 新电商正面迎战“阅读焦虑”