前言:
记住manacher是一个很简单的算法。
首先我们来了解一下回文字串的定义:若一个字符串中的某一子串满足回文的性质,则称其是回文子串。(注意子串必须是连续的,而子序列是可以不连续的)
那么若给定一长度为n的字符串,要求出最长回文子串的长度,怎么做呢?
首先想到的是暴力搜索,我就不赘述思路了。那如果n特别大呢?10的7次方怎么做?
于是,我们需要了解一个贼有意思的鸡肋算法manacher,俗称“马拉车”,为什么说是贼有意思呢?因为它的思路实在是巧妙,又为什么说是鸡肋呢?因为它貌似只适用于求解最大回文子串的问题。
 
思路:

  首先我们知道回文子串的判定和长度的奇偶性是有关系的,由于回文分为偶回文(比如 bccb)和奇回文(比如 bcacb),而在处理奇偶问题上会比较繁琐,所以这里我们使用一个技巧,在字符间插入一个字符(前提这个字符未出现在串里),常用的是"$""#"。举个例子:s="abbahopxpo",转换为newS="$#a#b#b#a#h#o#p#x#p#o#\0"(这里在串首、尾加的字符"$"和"\0";只是设置边界,为了防止越界,而且显然不影响回文子串,下面会有说明),如此,s 里起初有一个偶回文abba和一个奇回文opxpo,被转换为"#a#b#b#a#"和"#o#p#x#p#o#",长度都转换成了奇数。

  证明经过上述操作回文串的长度必为奇数:

    若原回文串的长度为奇数n(原串 aaa),首尾间共有偶数n-1个空位被加上"#",加上首前和尾后各1个"#"(新串 #a#a#a#),可见新的长度为2n+1,显然是奇数;

    若原回文串的长度为偶数n(原串 aa),首尾间共有奇数n-1个空位被加上"#",加上首前和尾后各1个"#"(新串 #a#a#),可见新的长度也为2n+1,显然是奇数。得证。

  我们定义一个辅助数组int p[]p[i]表示以news[i]为中心的最长回文的半径,例如

  易得P[i]-1即以i为中心的在原串中的回文子串的长度:例如P[5]=5,则4就是以5这个位置为中心在原串中的最长回文子串的长度(abba),为什么这是对的呢?因为我们知道P[i]*2-1为新串中以i为中心的最长回文子串的长度,设该回文子串原长为n,则由上面的证明可知新串的长度=2n+1=P[i]*2-1,移项化简得n=P[i]-1。

  于是重点来了:我们如何快速的求出P[]数组。这时就要用到DP的思想了,我们一般会想到这样求解p[i],先初始化p[i]=1,再以news[i]为中心判断两边是否相等,相等就p[i]++。这就是普通的思维,但是我们想想,能否避免重复操作让p[i]的初始化不是 1,让它更大点,看下图:

  设置两个变量,mx 和 id 。
  mx 代表以news[id]为中心的最长回文最右边界,也就是mx=id+p[id]。

  假设我们现在求p[i],也就是以news[i]为中心的最长回文半径,如果i<mx,如上图,那么

  if(i<mx) p[i] = min(p[id*2-i] , mx-i);

  else p[i] = 1;

 怎么理解呢?我们看图,因为mx是以id为中心的最长回文半径,若当前的i比mx要小,说明以i为中心的最长回文子串的一部分已经出现在以id为中心的回文子串中了,注意图中下面标注的两条短黑线,因为我们是线性dp,j点一定被访问过且P[j]被处理过,而j与i关于id对称(i+j=2*id),所以j=id*2-i,由于回文串的对称性,以i为中心的最长回文子串中的半径最小值一定是p[j]和mx-i中的最小值; 而若i>mx,就只能将p[i]赋为1来更新了。(感觉解释了和没解释一样啊,由于博主表述能力较差,我们不如举例)

  就比如一个回文子串:"#a#a#a#a#a#",我们以中间的a位置为id,所以id=6,mx=12。假设目前访问到了i=8的位置,则与其对称的j=2*6-8=4,而p[4]在访问8之前已经处理,p[4]=4,mx-i=4,所以取最小值4,将p[8]初始值赋为4。因为很容易看出在当前已经可以确定的是以8为中心的最长回文串的半径至少为4(#a#a#a#),当然有可能更大,我们之后判断news[i+P[i]]==news[i-P[i]]是否成立,若成立就p[i]++。

  讲的好心累啊,我学manacher时完全自学,也没有什么解释,完全是靠自己看懂的,还是得自己结合图和代码理解啊,先发一波核心代码。

int manacher()
{
int len=init();
int ans=-N,id,mx=;
for(int i=;i<len;i++)
{
if(i<mx)p[i]=min(p[id*-i],mx-i);
else p[i]=;
while(news[i-p[i]]==news[i+p[i]])p[i]++;
if(mx<i+p[i])id=i,mx=i+p[i];
ans=max(ans,p[i]-);
}
return ans;
}

  再发两种情况的图片自行理解一番:

  这是初值p[i]=p[id*2-i]的图:

   这是初值p[i]=mx-i的图,注意虚线部分:
 
 
巨说极其重要的性质


再来看这张图,我们发现,如果mx不更新,就不会出现本质不同的回文子串,因为前面已经出现过了;而每扩展一次mx,最多新出现一个本质不同的回文子串。
于是得到性质:一个字符串最多只有n个本质不同的回文子串。这个性质很重要,有些题会用到,需要这个性质去分析。

(虽然我也不知道有啥用……)

算法复杂度分析:知乎

我自己简略地讲一下,因为i与mx只有两种情况,而每次检索已经保证了单调递增,可以知道每个点最多被访问两次,while()循环本身的时间复杂度在没有前提条件的情况下确实是但是这里的(也就是上面答案中的),是不断往后走而不可能往前退的,它自身的值的变化是递增的。那么你可以明白,要进入while循环,的值必然是比大的,也就是说整个程序结束为止,while循环执行的操作数为次(线性次),而字符串中的每个字符,最多能被访问到2次。时间复杂度必然为

 

终于讲完了,上模板题洛谷P3805

题目描述

给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度.

字符串长度为n

输入输出格式

输入格式:

一行小写英文字符a,b,c...y,z组成的字符串S

输出格式:

一个整数表示答案

输入输出样例

输入样例#1:

aaa
输出样例#1:

3

说明

字符串长度len <= 11000000

代码:

#include<bits/stdc++.h>
#define il inline
#define ll long long
#define debug printf("%d %s\n",__LINE__,__FUNCTION__)
using namespace std;
const int N=;
char s[N],news[N];
int p[N];
il int init()
{
int len=strlen(s);
news[]='$',news[]='#';
int j=;
for(int i=;i<len;i++)news[j++]=s[i],news[j++]='#';
news[j]='\0';
return j;
}
il int manacher()
{
int len=init();
int ans=-N,id,mx=;
for(int i=;i<len;i++)
{
if(i<mx)p[i]=min(p[id*-i],mx-i);
else p[i]=;
while(news[i-p[i]]==news[i+p[i]])p[i]++;
if(mx<i+p[i])id=i,mx=i+p[i];
ans=max(ans,p[i]-);
}
return ans;
}
int main()
{
scanf("%s",s);
printf("%d",manacher());
return ;
}

manacher算法详解+模板 P3805的更多相关文章

  1. manacher算法 详解+模板

    manacher算法可以解决字符串的回文子串长度问题. 个人感觉szy学长讲的非常好,讲过之后基本上就理解了. 那就讲一下个人的理解.(参考了szy学长的ppt) 如果一个回文子串的长度是偶数,对称轴 ...

  2. 算法进阶面试题01——KMP算法详解、输出含两次原子串的最短串、判断T1是否包含T2子树、Manacher算法详解、使字符串成为最短回文串

    1.KMP算法详解与应用 子序列:可以连续可以不连续. 子数组/串:要连续 暴力方法:逐个位置比对. KMP:让前面的,指导后面. 概念建设: d的最长前缀与最长后缀的匹配长度为3.(前缀不能到最后一 ...

  3. Manacher算法详解

    问题 什么是回文串,如果一个字符串正着度读和反着读是一样的,这个字符串就被称为回文串. such as noon level aaa bbb 既然有了回文,那就要有关于回文的问题,于是就有了-- 最长 ...

  4. 经典算法 Manacher算法详解

    内容: 1.原始问题   =>O(N^2) 2.Manacher算法   =>O(N) 1.原始问题 Manacher算法是由题目“求字符串中长回文子串的长度”而来.比如 abcdcb 的 ...

  5. [转] Manacher算法详解

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

  6. hdu3068之manacher算法+详解

    最长回文 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submi ...

  7. KM算法详解+模板

    http://www.cnblogs.com/wenruo/p/5264235.html KM算法用来求二分图最大权完美匹配. 本文配合该博文服用更佳:趣写算法系列之--匈牙利算法 现在有N男N女,男 ...

  8. KM算法 详解+模板

    先说KM算法求二分图的最佳匹配思想,再详讲KM的实现.[KM算法求二分图的最佳匹配思想] 对于具有二部划分( V1, V2 )的加权完全二分图,其中 V1= { x1, x2, x3, ... , x ...

  9. KMP算法 详解+模板

    本文大部分摘自szy学长的ppt<string>中的KMP部分. %%%膜拜szy大神orz 1.概述 KMP 算法是用来解决单模匹配问题的一种算法. 如果暴力的进行单模匹配,那么时间复杂 ...

随机推荐

  1. Asp.net Web Api开发Help Page 添加对数据模型生成注释的配置和扩展

    在使用webapi框架进行接口开发的时候,编写文档会需要与接口同步更新,如果采用手动式的更新的话效率会非常低.webapi框架下提供了一种自动生成文档的help Page页的功能. 但是原始版本的效果 ...

  2. Eclipse - 配置优化

    去除不需要的启动加载项 Window --> Preferences -->General --> Startup and Shutdown 关闭自动更新 Window --> ...

  3. protected修饰符详解

    protected这个修饰符,各大参考书都会这样说:访问权限为类内,包内和子类,因此在父类中定义的方法和成员变量如果为protected修饰的,是可以在不同包中的子类进行访问的,示例代码如下: pac ...

  4. 一种精准monkey测试的方法

    WeTest 导读 相信大家都知道移动端应用的monkey测试吧,不知你们有没有为monkey测试的太过于随机性的特性有过困扰,至少在我们这种界面控件较少且控件位置较偏的app的使用上其测试有效性大打 ...

  5. 根据wsdl生成服务端代码

    场景描述 最近在和一家公司做业务接口对接,由他们那边回调我们这边,对方直接扔过来一个webservice的wsdl文件,让我们按照他们的规范来做webservice服务, 大多数的对接应该是我们创建完 ...

  6. hdu1010Tempter of the Bone(迷宫dfs+剪枝)

    Tempter of the Bone Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Othe ...

  7. APP上下左右滑动屏幕的处理

    #获得机器屏幕大小x,y driver = self.driver def getSize(): x = driver.get_window_size()['width'] y = driver.ge ...

  8. 使用unittest里面的discover()方法组织测试用例

    import osimport unittest directory = os.getcwd()# 测试用例的目录organize = unittest.defaultTestLoader.disco ...

  9. JMeter常用元器件

    测试计划, 是整个工程的根节点, 可以取别名, 并添加注释, 里面的设置是全局变量: 线程组, 是一组线程的集合, 可以取别名, 并添加注释, 里面的设置只对本线程组有效: HTTP请求, 也就是取样 ...

  10. Bootstrap框架(组件)

    按钮组 通过按钮组容器把一组按钮放在同一行里.通过与按钮插件联合使用,可以设置为单选框或多选框的样式和行为. 按钮组中的工具提示和弹出框需要特别的设置 当为 .btn-group 中的元素应用工具提示 ...