Able was I ere I saw Elba.     ----Napoléon Bonaparte(拿破仑)

一、回文串&回文子串

   这个很好理解。

  如果一个字符串正着读和反着读是一样的,那它就是回文串。 eg. abba ;

  如果一个字符串 S 的子串 SS 为回文串,那么 SS 即为 S 的回文子串;若 SSS 为 S 的回文子串中最长的一个,那么我们称 SSS 为 S 的最长回文子串

二、Manacher算法

  如何找到一个字符串的最长回文子串呢?

  我们很容易想到一个 O(n2) 的方法,即:从每个字符开始向两边爆搜。但显然,这个方法效率太低下了。

  如何快速求出答案,这就是 Manacher算法的事了。

  Ⅰ. 奇回文&偶回文

    字面意思,不再赘述。

    如果同时存在奇回文和偶回文,那么处理起来会比较的繁琐,下面就是Manacher 一个很巧妙的方法了:在字符串收尾,即各字符间插入一个特殊的字符(指没有出现过的字符),例如:

aba  ----> #a#b#a#
abba ----> #a#b#b#a#

    这样,所有的回文串就都成奇回文了。

inline int Pre()
{
S[0]='@',S[1]='#';
int j(1);
for(register int i=0,len=IP.size();i<len;++i) S[++j]=IP[i],S[++j]='#';
S[++j]='\0';return j;
}

预处理

  Ⅱ. 最长回文半径

    令一个回文串中最左或最右位置的字符与其对称轴的距离称为 " 回文半径 ",令 R [ i ] 表示字符 i 的最长回文半径。

i 0 1 2 3 4 5 6 7 8 9 10
S @ a # b # b # a # b $
R[i] 边界 1 1 2 4 2 1 4 1 2 边界

    显然,R [ i ] - 1 即为以 i 为中心的最长回文子串的长度。

    那么,我们要求的最长回文子串,就成了 max { R [ i ] - 1 } 。

    如何快速求出 R [ ] ???

  Ⅲ. Manacher

    从左往右依次讨论。

    设 Max 为已讨论的子串中能达到的右端最大位置,P 为提供当前 Max 的字符位置。如下表:

i 0 1 2 3 4 5 6 7 8 9 10 11  12  13  14  15 16 17 18 19 20
          Max的对称点                  Max            

    

    接下来讨论的位置 i 可以分为两种情况:小于等于 Max  ,大于 Max:

      1. 小于等于 Max :

i 0 1 2 3 4 5 6 7 8 9 10 11  12  13  14  15 16 17 18 19 20
          Max的对称点     j(i的对称点)       i      Max            

      由于 i 和 j 对称 ,所以求出 i 的对称点 j ,简直是 "轻易而举" 。

      

      不难发现现在有可以分为两种情况讨论:

         ( 1 ) . 以 j 为对称轴的回文串比较短:那么可以直接令 R [ i ] = R [ j ] ;

          ( 2 ) . 以 j 为对称轴的回文串比较长:此时,我们只能确定不超过 Max 的部分的情况,对于 Max 以外的,我们需要以 i 为中心开始往两侧拓展,直到两侧不同,同时更新 p 和 Max。

       

       2. 大于 Max :  这种情况很好处理,直接拓展就可以了(当然,要同时更新 p 和 Max)

  Ⅳ. 代码

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
char S[50000005];
int m,R[50000005];
string IP;
inline int Pre()
{
S[0]='@',S[1]='#';
int j(1);
for(register int i=0,len=IP.size();i<len;++i) S[++j]=IP[i],S[++j]='#';
S[++j]='\0';return j;
}
inline int Manacher()
{
m=Pre();
int RET(-1),x(0),Max(0);
for(register int i=1;i<m;++i)
{
if(i<Max) R[i]=min(R[2*x-i],Max-i+1);
else R[i]=1;
while(S[i-R[i]]==S[i+R[i]]) ++R[i];
if(Max<i+R[i]-1) x=i,Max=i+R[i]-1;
RET=max(RET,R[i]-1);
}
return RET;
}
int main()
{
ios::sync_with_stdio(false);
cin>>IP,cout<<Manacher();
return 0;
}

Manacher

  Ⅴ. 时间复杂度

    由于Max是不断向右拓展的,最多拓展 n 次,不难得出 马拉车 的时间复杂度是线性的,即 O( n )。

最新文章

  1. MySQL 使用JOIN优化子查询
  2. linux命令(4):mkdir命令
  3. [moka同学笔记]yii2.0 advanced高级版 安装配置 与 rbac (Ⅰ)
  4. LNK2005 连接错误解决办法
  5. MVC 学习系列-Controller
  6. RANSAC和Flitline
  7. 如何用C++语言编程(How to program in C++)
  8. Linux命令zip和unzip
  9. HDU 1166 敌兵布阵 (线段树 单点更新)
  10. Sybase数据库截断和清空日志的方法
  11. Spring中ApplicationContext加载机制
  12. MySQL 性能优化的最佳20多条经验分享(二)(转)
  13. 一起学Linux03之Linux系统目录结构
  14. oracle03
  15. 使用zii.widgets.CDetailView显示内容
  16. Pyhon 逻辑运算符
  17. javascript多语言设定方式
  18. MySQL 5.7的多源复制
  19. 远程桌面中Tab键不能补全的解决办法
  20. vue全家桶+Koa2开发笔记(6)--app开发

热门文章

  1. xxl-job &lt;=2.0.2 反序列化漏洞
  2. 获取office版本
  3. CodeForce-811C Vladik and Memorable Trip(动态规划)
  4. HCNP Routing&amp;Switching之路由过滤工具Filter-Policy
  5. PHP中国际化的字符串比较对象
  6. PHP中的文件系统函数(二)
  7. PHP中的数组分页实现(非数据库)
  8. jmeter之图形结果
  9. PHP - 设计模式 - 观察者模式
  10. phpQuery