学习博客:https://www.cnblogs.com/love-yh/p/7072161.html

首先,得先了解什么是回文串(我之前就不是很了解,汗)。回文串就是正反读起来就是一样的,如“abba”。关于采用时间复杂度为O(n^2),以每个字符为中心去向两端遍历寻找最大回文串的方法,可以见我之前些的博客,戳这里

当我们遇到字符串为“aaaaaaaaa”,之前的算法就会发生各个回文相互重叠的情况,会产生重复计算,然后就产生了一个问题,能否改进?答案是能,1975年,一个叫Manacher发明了Manacher Algorithm算法,俗称马拉车算法,其时间复杂为O(n)。该算法是利用回文串的特性来避免重复计算的,至于如何利用,且由后面慢慢道来。

在时间复杂度为O(n^2)的算法中,我们在遍历的过程要考虑到回文串长度的奇偶性,比如说“abba”的长度为偶数,“abcba”的长度为奇数,这样在寻找最长回文子串的过程要分别考奇偶的情况,是否可以统一处理了?

马拉车算法:

一)第一步是改造字符串S,变为T,其改造的方法如下:

在字符串S的字符之间和S的首尾都插入一个“#”,如:S=“abba”变为T="#a#b#b#a#" 。我们会发现S的长度是4,而T的长度为9,长度变为奇数了!!那S的长度为奇数的情况时,变化后的长度还是奇数吗?我们举个例子,S=“abcba”,变化为T=“#a#b#c#b#a#”,T的长度为11,所以我们发现其改造的目的是将字符串的长度变为奇数,这样就可以统一的处理奇偶的情况了

二)第二步,为了改进回文相互重叠的情况,我们将改造完后的T[ i ] 处的回文半径存储到数组P[ ]中,P[ i ]为新字符串T的T[ i ]处的回文半径,表示以字符T[i]为中心的最长回文字串的最端右字符到T[i]的长度,如以T[ i ]为中心的最长回文子串的为T[ l, r ],那么P[ i ]=r-i+1。这样最后遍历数组P[ ],取其中最大值即可。若P[ i ]=1表示该回文串就是T[ i  ]本身。举一个简单的例子感受一下:

数组P有一性质,P[ i ]-1就是该回文子串在原字符串S中的长度 ,那就是P[i]-1就是该回文子串在原字符串S中的长度,至于证明,首先在转换得到的字符串T中,所有的回文字串的长度都为奇数,那么对于以T[i]为中心的最长回文字串,其长度就为2*P[i]-1,经过观察可知,T中所有的回文子串,其中分隔符的数量一定比其他字符的数量多1,也就是有P[i]个分隔符,剩下P[i]-1个字符来自原字符串,所以该回文串在原字符串中的长度就为P[i]-1。【这段解释引用 dyx心心

另外,由于第一个和最后一个字符都是#号,且也需要搜索回文,为了防止越界,我们还需要在首尾再加上非#号字符,实际操作时我们只需给开头加上个非#号字符,结尾不用加的原因是字符串的结尾标识为'\0',等于默认加过了。这样原问题就转化成如何求数组P[ ]的问题了。

三)如何求数组P [ ]

从左往右计算数组P[ ], Mi为之前取得最大回文串的中心位置,而R是最大回文串能到达的最右端的值。

1)当 i <=R时,如何计算 P[ i ]的值了?毫无疑问的是数组P中点 i 之前点对应的值都已经计算出来了。利用回文串的特性,我们找到点 i 关于 Mi 的对称点 j ,其值为 j= 2*Mi-i 。因,点 j 、i 在以Mi 为中心的最大回文串的范围内([L ,R]),

a)那么如果P[j] <R-i (同样是L和j 之间的距离),说明,以点 j 为中心的回文串没有超出范围[L ,R],由回文串的特性可知,从左右两端向Mi遍历,两端对应的字符都是相等的。所以P[ j ]=P[ i ](这里得先从点j转到点i 的情况),如下图:

b)如果P[ j ]>=R-i (即 j 为中心的回文串的最左端超过 L),如下图所示。即,以点 j为中心的最大回文串的范围已经超出了范围[L ,R] ,这种情况,等式P[ j ]=P[ i ]还成立吗?显然不总是成立的!因,以点 j 为中心的回文串的最左端超过L,那么在[ L, j ]之间的字符肯定能在( j, Mi ]找到相等的,由回文串的特性可知,P[ i ] 至少等于R- i,至于是否大于R-i(图中红色的部分),我们还要从R+1开始一一的匹配,直达失配为止,从而更新R和对应的Mi以及P[ i ]。

2)当 i > R时,如下图。这种情况,没法利用到回文串的特性,只能老老实实的一步步去匹配。

相应的代码如下:

  1. #include<iostream>
  2. #include<vector>
  3. using namespace std;
  4. string Manacher(string s)
  5. {
  6. /**改造字符串*/
  7. int len=s.size();
  8. string res="$#";
  9. for(int i=;i<len;i++)
  10. {
  11. res+=s[i];
  12. res+='#';
  13. }
  14. // cout<<res<<endl; //改造后的串
  15.  
  16. /** 数组 */
  17. vector<int>P(res.size(),);
  18. int Mid=,R=;//Mid为当前选中的回文串的中心 R为当前选中的回文串能到达的最右端的位置
  19. int maxLen=,maxPoint=;//最大回文串长度 最大回文串中心点
  20. for(int i=;i<res.size();i++)
  21. {
  22. P[i]=R>i?min(P[*Mid-i],R-i):;
  23. while(res[i+P[i]]==res[i-P[i]]) P[i]++;//+个$的作用出来了 这样就不会越界了
  24. if(R<i+P[i]) //超过了最右端 则改变中心点和对应的最右端
  25. {
  26. R=i+P[i];
  27. Mid=i;
  28. }
  29. if(maxLen<P[i])//更新最大回文串长度 并记下此时的中心
  30. {
  31. maxLen=P[i];
  32. maxPoint=i;
  33. }
  34.  
  35. }
  36. // cout<<"*"<<maxLen<<endl;
  37. return s.substr((maxPoint-maxLen)/,maxLen-);
  38.  
  39. }
  40. int main()
  41. {
  42. string s;
  43. cin>>s;
  44. cout<<Manacher(s)<<endl;
  45. return ;
  46. }

Manacher算法(马拉车)的更多相关文章

  1. Manacher算法 (马拉车算法)

    #include<iostream> #include<string.h> #include<algorithm> using namespace std; ]; ...

  2. Manacher算法学习 【马拉车】

    好久没写算法学习博客了 比较懒,一直在刷水题 今天学一个用于回文串计算问题manacher算法[马拉车] 回文串 回文串:指的是以字符串中心为轴,两边字符关于该轴对称的字符串 ——例如abaaba 最 ...

  3. manacher(马拉车)算法详解+例题一道【bzoj3790】【神奇项链】

    [pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=39091399 (CSDN好像有bug,不知道为什 ...

  4. manacher(马拉车)算法

    断断续续地看了两天的马拉车算法,可算是给搞明白了(贼开心),这算是自己搞懂的第一个算法了(23333333333333)这个算法照目前自己的理解来看,貌似就只能求个字符串中的回文串(接触这个算法是要求 ...

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

    Manacher算法求最长回文字串 算法思路 按照惯例((・◇・)?),这里只是对算法的一些大体思路做一个描述,因为找到了相当好理解的博客可以参考(算法细节见参考文章). 一般而言,我们的判断回文算法 ...

  6. Manacher算法(马拉车算法)浅谈

    什么是Manacher算法? 转载自百度百科 Manachar算法主要是处理字符串中关于回文串的问题的,它可以在 O(n) 的时间处理出以字符串中每一个字符为中心的回文串半径,由于将原字符串处理成两倍 ...

  7. manacher算法_求最长回文子串长度

    很好的总结,转自: http://blog.csdn.net/dyx404514/article/details/42061017 总结为:两大情况,三小情况. 两大情况:I. i <= p 1 ...

  8. [转] Manacher算法详解

    转载自: http://blog.csdn.net/dyx404514/article/details/42061017 Manacher算法 算法总结第三弹 manacher算法,前面讲了两个字符串 ...

  9. Manacher算法学习笔记 | LeetCode#5

    Manacher算法学习笔记 DECLARATION 引用来源:https://www.cnblogs.com/grandyang/p/4475985.html CONTENT 用途:寻找一个字符串的 ...

  10. 最长回文子串问题-Manacher算法

    转:http://blog.csdn.net/dyx404514/article/details/42061017 Manacher算法 算法总结第三弹 manacher算法,前面讲了两个字符串相算法 ...

随机推荐

  1. [Lua快速了解一下]Lua的控制语句

    -Lua中没有++或者--的骚操作 -while loop sum = num = do sum = sum + num num = num + end print("sum =" ...

  2. SharePoint配置网站集的审核设置

    配置网站集的审核设置 您可以使用 Microsoft SharePoint Server 2010 的审核功能来跟踪哪些用户对网站集的网站.内容类型.列表.库.列表项和库文件执行了哪些操作.了解谁对哪 ...

  3. .net List<T>

    List的几个方法 List=>List.Find()List.FindAll()List.Contains() List.ForEach()List.ConvertAll() 1. 先比较Fi ...

  4. [.net 多线程]volatile 摘录

    一.volatile 介绍 volatile 关键字指示一个字段可以由多个同时执行的线程修改. 声明为 volatile 的字段不受编译器优化(假定由单个线程访问)的限制. 这样可以确保该字段在任何时 ...

  5. Mysql避免重复插入记录方法

    一.mysql replace用法 1.replace into  replace into table (id,name) values('1','aa'),('2','bb')  此语句的作用是向 ...

  6. 复制构造函数被调用的三种情况------新标准c++程序设计

    1.当用一个对象去初始化同类的另一个对象时,会引发复制构造函数被调用.例如,下面的两条语句都会引发复制构造函数的调用,用以初始化c2. C c2 (c1); C c2=c1; 这两条语句是等价的.注意 ...

  7. OOP2(虚函数/抽象基类/访问控制与继承)

    通常情况下,如果我们不适用某个函数,则无需为该函数提供定义.但我们必须为每个虚函数都提供定义而不管它是否被用到了,这因为连编译器也无法确定到底会适用哪个虚函数 对虚函数的调用可能在运行时才被解析: 当 ...

  8. 题解 P3386 【【模板】二分图匹配】

    题目链接 首先呢声明一下,本宝宝发这篇题解只是为了(goto a;) 个人还是比较喜欢跑dinic暴力跑最大流...竟然比匈牙利还快..如果说不懂网络流的~~蒟蒻~~大佬们.可以看看这个(反正我就是在 ...

  9. luoguP3835 [模板]可持久化平衡树

    https://www.luogu.org/problemnew/show/P3835 因为博主精力和实力有限,学不懂 fhq treap 了,因此只介绍 leafy tree 解法 leafy tr ...

  10. [转载]ssget 用法详解 by yxp

    总结得很好的ssget用法.....如此好文,必须转载. 原文地址: http://blog.csdn.net/yxp_xa/article/details/72229202 ssget 用法详解 b ...