转自:https://www.cnblogs.com/grandyang/p/4475985.html

这个马拉车算法Manacher‘s Algorithm是用来查找一个字符串的最长回文子串的线性方法,由一个叫Manacher的人在1975年发明的,这个方法的最大贡献是在于将时间复杂度提升到了线性,这是非常了不起的。对于回文串想必大家都不陌生,就是正读反读都一样的字符串,比如 “bob”, “level”, “noon” 等等,那么如何在一个字符串中找出最长回文子串呢,可以以每一个字符为中心,向两边寻找回文子串,在遍历完整个数组后,就可以找到最长的回文子串。但是这个方法的时间复杂度为O(n*n),并不是很高效,下面我们来看时间复杂度为O(n)的马拉车算法。

由于回文串的长度可奇可偶,比如"bob"是奇数形式的回文,"noon"就是偶数形式的回文,马拉车算法的第一步是预处理,做法是在每一个字符的左右都加上一个特殊字符,比如加上’#’,那么

bob --> #b#o#b#

noon --> #n#o#o#n#

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

串: #1 # 2 # 2 # 1 # 2 # 2 #
p[i]: 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。

那么下面我们就来看如何求p数组,需要新增两个辅助变量mx和id,其中id为能延伸到最右端的位置的那个回文子串的中心点位置,mx是回文串能延伸到的最右端的位置,这个算法的最核心的一行如下:

p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;

可以这么说,这行要是理解了,那么马拉车算法基本上就没啥问题了,那么这一行代码拆开来看就是

如果 mx > i, 则 p[i] = min( p[2 * id - i] , mx - i )

否则,p[i] = 1

当 mx - i > P[j] 的时候,以S[j]为中心的回文子串包含在以S[id]为中心的回文子串中,由于 i 和 j 对称,以S[i]为中心的回文子串必然包含在以S[id]为中心的回文子串中,所以必有 P[i] = P[j],见下图。

当 P[j] >= mx - i 的时候,以S[j]为中心的回文子串不一定完全包含于以S[id]为中心的回文子串中,但是基于对称性可知,下图中两个绿框所包围的部分是相同的,也就是说以S[i]为中心的回文子串,其向右至少会扩张到mx的位置,也就是说 P[i] = mx - i。至于mx之后的部分是否对称,就只能老老实实去匹配了。

对于 mx <= i 的情况,无法对 P[i]做更多的假设,只能P[i] = 1,然后再去匹配了。

参见如下实现代码:

#include <vector>
#include <iostream>
#include <string>using namespace std;string Manacher(string s) {// Insert '#'string t = "$#";for (int i = 0; i < s.size(); ++i) {t += s[i];t += "#";}// Process tvector<int> p(t.size(), 0);int mx = 0, id = 0, resLen = 0, resCenter = 0;for (int i = 1; i < t.size(); ++i) {p[i] = mx > i ? min(p[2 * id - i], mx - i) : 1;while (t[i + p[i]] == t[i - p[i]]) ++p[i];if (mx < i + p[i]) {mx = i + p[i];id = i;}if (resLen < p[i]) {resLen = p[i];resCenter = i;}}return s.substr((resCenter - resLen) / 2, resLen - 1);
}int main() {string s1 = "12212";cout << Manacher(s1) << endl;string s2 = "122122";cout << Manacher(s2) << endl;string s = "waabwswfd";cout << Manacher(s) << endl;
}

Manacher's Algorithm 马拉车算法相关推荐

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

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

  2. Manacher Algorithm马拉车算法详解

    Manacher Algorithm马拉车算法详解 链接:https://www.zhihu.com/question/37289584/answer/465656849 中心扩展算法 我们先来看一个 ...

  3. 字符串-Manacher算法(你知道马拉车算法吗?)

    文章目录 原理 奇偶问题 p[]数组 马拉车求p[] 模板 例题 P3805 [模板]manacher算法 P1659 拉拉队排练 原理 马拉车算法当然不是马拉着车的奇奇怪怪的东西,是Manacher ...

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

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

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

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

  6. Manacher(马拉车)算法—简略讲解

    这是一篇菜鸡的算法小笔记!希望你喜欢! 前言 马拉车算法是用来查找一个字符串的最长回文子串的线性方法,是一个叫 Manacher 的人在 1975 年发明的,这个方法的最大贡献是在于将时间复杂度提升到 ...

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

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

  8. manacher算法详解(马拉车算法)

    马拉车算法 Manacher算法是由题目"求字符串中最长回文子串的长度"而来.比如 abcdcb 的最长回文子串为 bcdcb ,其长度为5. 回文:正着念,反着念都一样 暴力解不 ...

  9. 【算法】Manacher(马拉车)算法

    原jekyll 2019-09-07 Manacher's Alogrithm,中文名叫马拉车算法,是一位叫Manacher的人在1975年提出的一种算法,解决的问题是求最长回文子串,算法的神奇之处就 ...

最新文章

  1. android 贝塞尔曲线点击区域,白话经典贝塞尔曲线及其在 Android 中的应用
  2. C++ BigInt模板手打
  3. 使用Hadoop Streaming 完成MapReduce(Python代码)
  4. 【翻译】.NET 5 Preview5发布
  5. Spring Cloud构建微服务架构:服务容错保护(Hystrix服务降级)【Dalston版】 1
  6. 在 NetBeans IDE 6.0 中分析 Java 应用程序性能
  7. android studio发包,androidstudio使用gradle打包配置详解.pdf
  8. 地震数据读写segyio的脑图(c语言接口)
  9. c语言链栈,链栈 C语言实现
  10. arduino和轮毂电机接线_Arduino六足机器人第一部分—机制与接线
  11. MATLAB 图像滤波器
  12. jdk1.8新特性:函数式接口、方法引用、函数式编程、常用函数式接口
  13. 【数据结构基础_双向链表(有[*pHead]和[*pEnd])_(C语言实现)】
  14. 公用电信网间互联管理规定
  15. 逐飞K66核心板+逐飞DAP下载器调试方式及接线(SWD)
  16. NOIP模拟 葫芦(分数规划)
  17. 【HTML】html基本标签-1(文字,列表,图片标签)
  18. 背包模块的设计(日常任务模块, 武器排行榜, 战术, 英雄战斗力, 活动模块)
  19. 推荐几个免费看动漫的网站
  20. USER_用户_数据库知识点

热门文章

  1. xml java jaxb_【Java】JAXB操作XML用法详解
  2. 嵌入式Linux初学者眼中的世界
  3. 软件测试简单经验心得分享
  4. 与AI一起享受创作音乐的乐趣| Mixlab人工智能音乐
  5. Zookeeper的学习与应用
  6. 「文献解读」GhADF6介导的肌动蛋白重组与棉花对黄萎病菌的抗病性
  7. (二)Python-tkinter桌面应用(桌面上的弹幕)
  8. 力扣一日一练(7)——爬楼梯(斐波那契数列)
  9. python random模块下载,Python的random模块
  10. codemirror6 不支持viewportMargin配置