Manacher

一、背景

1975年,Manacher发明了Manacher算法(中文名:马拉车算法),是一个可以在O(n)的复杂度中返回字符串s中最长回文子串长度的算法,十分巧妙。

让我们举个栗子,栗子:

1.字符串:abbababa        最长回文子串:5(abbababa

2.字符串:abcbbabbc      最长回文子串:7(abcbbabbc

3.字符串:abccbaba        最长回文子串:6(abccbaba)

传统方法是,遍历每个字符,以该字符为中心向两边查找。时间复杂度为O(n^2),效率很差;

而这个神奇的Manacher算法将复杂度提升到了O(n)。

来一起瞅一瞅它是如何工作的吧。

二、算法过程分析

回文分为奇回文(ababa)和偶回文(abba),这里比较难以处理,我们使用一个(sao)(cao)(zuo)(很重要)。我们将字符串首尾和每个字符间插入一个字符(注意:这个自符在串中并未出现)例如:'#'.

栗子!栗子!s='abbadcacda'先转化成s_new='$#a#b#b#a#d#c#a#c#d#a#\0'('$'与'\0',是边界,下面的代码中可以看到)

这样原串中的偶回文(abba)与奇回文(adcacda),变成了(#a#d#d#a#)与(#a#d#c#a#c#d#a#)两个奇回文

定义数组p[],用p[i]表示以i为中心的最长回文半径。栗子在这里:

那,p[i]该如何求呢?很显然,p[i]-1正好就是原字符中的最长回文串长度了。

让我们一起找到正解。

请看下图:

定义两个变量mx和id。mx就是以id为中心的最长回文右边界,也就是mx=id+p[id],随后我们需要mx做出它的最大贡献。

假设我们在求p[i](以i为中心的最长回文半径),如果i<mx(如上图),那么我们就用mx和j来更新到我们已知的可以更新的最大长度,代码如下:

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

2*id-i是i关于id的对称点(上图j)(证明:i-id=id-j),而p[j]表示以j为中心的最长回文半径,这样我们就可以利用p[j]和mx加快速度了。

为什么要用p[j]和mx-i取min来更新,什么鬼?

淡定,淡定。我们想一下,p[j](以j为中心的最长回文半径)是已经知道了(因为是从前面扫过来的),若是p[j]>mx-i,我们是可以知道以j为中心,以mx的对称点到j的距离为半径形成的回文字符串是肯定存在的,并且id的左边直到mx的对称点与id的右边 直到mx是一一对应的,不难理解mx是i目前可以更新到的最大回文半径;若p[j]<mx-i,证明j的回文半径不到mx的对称点到j的距离,再次通过(id的左边直到mx的对称点与id的右边 直到mx是一一对应的),不难想到p[i]=p[j]。

取完min就是最大的回文半径吗?

显然不是,接下来的暴力往后扫就好了(学oi的都有暴力倾向)。

三、代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm> using namespace std; char s[11000002];
char s_new[21000002];//存添加字符后的字符串
int p[21000002]; int Init() {//形成新的字符串
int len=strlen(s);//len是输入字符串的长度
s_new[0]='$';//处理边界,防止越界
s_new[1]='#';
int j=2;
for(int i=0;i<len;i++) {
s_new[j++]=s[i];
s_new[j++]='#';
}
s_new[j]='\0';//处理边界,防止越界(容易忘记)
return j;// 返回s_new的长度
} int Manacher() {//返回最长回文串
int len=Init();//取得新字符串的长度, 完成向s_new的转换
int max_len=-1;//最长回文长度
int id;
int mx=0;
for(int i=1;i<=len;i++) {
if(i<mx)
p[i]=min(p[2*id-i],mx-i);//上面图片就是这里的讲解
else p[i]=1;
while(s_new[i-p[i]]==s_new[i+p[i]])//不需边界判断,因为左有'$',右有'\0'标记;
p[i]++;//mx对此回文中点的贡献已经结束,现在是正常寻找扩大半径
if(mx<i+p[i]) {//每走移动一个回文中点,都要和mx比较,使mx是最大,提高p[i]=min(p[2*id-i],mx-i)效率
id=i;//更新id
mx=i+p[i];//更新mx
}
max_len=max(max_len,p[i]-1);
}
return max_len;
} int main()
{
scanf("%s",&s);
printf("%d",Manacher());
return 0;
}

四、复杂度

完结撒花(复杂度不会证明呀,因为我是蒟蒻)

Manacher(马拉车)————O(n)回文子串的更多相关文章

  1. Manacher算法——最长回文子串

    一.相关介绍 最长回文子串 s="abcd", 最长回文长度为 1,即a或b或c或d s="ababa", 最长回文长度为 5,即ababa s="a ...

  2. Manacher算法----最长回文子串

    题目描述 给定一个字符串,求它的最长回文子串的长度. 分析与解法 最容易想到的办法是枚举所有的子串,分别判断其是否为回文.这个思路初看起来是正确的,但却做了很多无用功,如果一个长的子串包含另一个短一些 ...

  3. 马拉车算法——求回文子串个数zoj4110

    zoj的测评姬好能卡时间.. 求回文子串的个数:只要把p[i]/2就行了: 如果s_new[i]是‘#’,算的是没有中心的偶回文串 反之是奇回文串 /* 给定两个字符串s,t 结论:s,t不相同的第一 ...

  4. Manacher 求最长回文子串算法

    Manacher算法,是由一个叫Manacher的人在1975年发明的,可以在$O(n)$的时间复杂度里求出一个字符串中的最长回文子串. 例如这两个回文串“level”.“noon”,Manacher ...

  5. manacher求最长回文子串算法

    原文:http://www.felix021.com/blog/read.php?2040 首先用一个非常巧妙的方式,将所有可能的奇数/偶数长度的回文子串都转换成了奇数长度:在每个字符的两边都插入一个 ...

  6. hdu 3068 最长回文(manacher&amp;最长回文子串)

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

  7. manacher hihoCoder1032 最长回文子串

    居然能够做到O(n)的复杂度求最长回文.,也是给跪了. 以下这个人把manacher讲的很好,,能够看看 http://blog.csdn.net/xingyeyongheng/article/det ...

  8. hdu 3068 最长回文 【Manacher求最长回文子串,模板题】

    欢迎关注__Xiong的博客: http://blog.csdn.net/acmore_xiong?viewmode=list 最长回文                                 ...

  9. manacher求最长回文子串算法模板

    #include <iostream> #include <cstring> #include <cstdlib> #include <stdio.h> ...

  10. 【LeetCode】5. Longest Palindromic Substring 最大回文子串

    题目: Given a string S, find the longest palindromic substring in S. You may assume that the maximum l ...

随机推荐

  1. 【转】Python调用C语言动态链接库

    转自:https://www.cnblogs.com/fariver/p/6573112.html 动态链接库在Windows中为.dll文件,在linux中为.so文件.以linux平台为例说明py ...

  2. LeetCode 26. 删除有序数组中的重复项

    双指针法 分析: 设置两个指针:p1,p2,初始p1指向数组的第一个元素,p2指向第二个元素 1)如果p1的值 == p2的值,就让p2后移一位 2)如果p1的值 != p2的值,修改p1的下一个元素 ...

  3. web.xml常用配置详解

    web.xml常用配置详解 context-param 指定 ServletContext(上下文) 配置文件路径,基本配置一般是Spring配置文件,或者是spring-security的配置文件. ...

  4. 老板让我重构项目,我想首先应该服务治理---eureka服务治理深入浅出

    目录 什么是服务治理 Eureka调用过程 Eureka单机注册 Eureka 单机启动 单机注册 集群注册 客户调用 Eureka集群注册 idea 如何同一个项目启动多次 Eureka自我保护 为 ...

  5. Beta设计和计划 —— NameNotFound

    需求再分析 1. 用户群体 经过用户(大多数是同学)的反馈,我们发现大家其实并不是十分明确我们要做的到底是什么.具体要怎么用.而实际上我们的典型用户也并不是学生群体,因此出现这些偏差也是很正常的,毕竟 ...

  6. [bug] Your password does not satisfy the current policy requirements

    参考 https://blog.csdn.net/zhanaolu4821/article/details/93622812

  7. [bug] idea @Override is not allowed when implementing interface method

    解决 将idea环境jdk设置一致 参考 https://blog.csdn.net/shenya2/article/details/50460447 https://www.cnblogs.com/ ...

  8. Linux中级之lvs三个模式的图像补充(nat,dr,tun)

    负载均衡(Load Balance)集群提供了一种廉价.有效.透明的方法,来扩展网络设备和服务器的负载.带宽.增加吞吐量.加强网络数据处理能力.提高网络的灵活性和可用性. (1)单台计算机无法承受大规 ...

  9. 设置添加SSH-(转自破男孩)

    很多朋友在用github管理项目的时候,都是直接使用https url克隆到本地,当然也有有些人使用 SSH url 克隆到本地.然而,为什么绝大多数人会使用https url克隆呢? 这是因为,使用 ...

  10. jackjson学习2+CVE-2019-14379漏洞分析

    最近想着分析jackson,jackson和fastjson有点相似,浅蓝大神的文章很好,个人受益匪浅 昨天简单说了下jackson的用法,现在继续拓扑,补充前置知识,前置知识补充的足够多,那么漏洞分 ...