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. 【目录】Java项目开发中的知识记录

    此篇文章为学习Java的目录,<a href="#"></>这种的是还没有写的文章.已经加a标签的是已经写完的.没写的文章急切需要的话可以直接留言,不是特别 ...

  2. vue2.0与3.0响应式原理机制

    vue2.0响应式原理 - defineProperty 这个原理老生常谈了,就是拦截对象,给对象的属性增加set 和 get方法,因为核心是defineProperty所以还需要对数组的方法进行拦截 ...

  3. .NET平台系列8 .NET Core 各版本新功能

    系列目录     [已更新最新开发文章,点击查看详细] .NET Core 自2016年6月27日发布第一个正式版本以来,它主打的跨平台和高性能特效吸引了许多开发者,包括Java.PHP等语言的开发者 ...

  4. C#类中方法的执行顺序

    有些中级开发小伙伴还是搞不太明白在继承父类以及不同场景实例化的情况下,父类和子类的各种方法的执行顺序到底是什么,下面通过场景的举例来重新认识下方法的执行顺序: (下面内容涉及到了C#中的继承,构造函数 ...

  5. Shell 脚本重启项目

    每次发打包好项目后都需要手动重启项目,写个Shell脚本一键重启项目 Shell 脚本 #!/bin/bash while getopts "n:p:" arg do case $ ...

  6. 通过Cloudflare开启全站https

    Cloudflare 添加域名后,会自动生成通用证书,快速开启全站HTTPS,服务端不用做任何修改,还可以选择多种开启模式 一.注册Cloudflare账号 直接打开网站:https://www.cl ...

  7. [bug] @Test注解无法使用

    参考 https://blog.csdn.net/lixiangxiang666/article/details/83745901

  8. 攻防世界(四)php_rce

    攻防世界系列:php_rce 1.打开题目 看到这个还是很懵的,点开任意连接都是真实的场景. 2.ThinkPHP5,这里我们需要知道它存在 远程代码执行的漏洞. ?s=index/\think\ap ...

  9. USB历代标准及接口发展

    USB历代标准及接口发展 浊酒一杯家万里关注 2017.11.20 14:54:21字数 3,684阅读 2,514 文章转自中关村在线 1,多功能正反插 苹果让大众认识Type-C "Ty ...

  10. vue+element-ui, el-upload组件 文件上传之前return false,会自动调用文件移除回调问题

    日常搬砖的时候,项目中在使用element-ui的上传组件,但是当我在文件上传文件之前的回调里面做了些文件格式的二次校验和文件大小的校验的时 然后 return false 会发现调用 文件移除的回调 ...