manacher算法详解+模板 P3805
记住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]]==new
s[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]的图:
再来看这张图,我们发现,如果mx不更新,就不会出现本质不同的回文子串,因为前面已经出现过了;而每扩展一次mx,最多新出现一个本质不同的回文子串。
于是得到性质:一个字符串最多只有n个本质不同的回文子串。这个性质很重要,有些题会用到,需要这个性质去分析。
(虽然我也不知道有啥用……)
算法复杂度分析:知乎
我自己简略地讲一下,因为i与mx只有两种情况,而每次检索已经保证了单调递增,可以知道每个点最多被访问两次,while()循环本身的时间复杂度在没有前提条件的情况下确实是但是这里的(也就是上面答案中的),是不断往后走而不可能往前退的,它自身的值的变化是递增的。那么你可以明白,要进入while循环,的值必然是比大的,也就是说整个程序结束为止,while循环执行的操作数为次(线性次),而字符串中的每个字符,最多能被访问到2次。时间复杂度必然为
终于讲完了,上模板题洛谷P3805
题目描述
给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度.
字符串长度为n
输入输出格式
输入格式:
一行小写英文字符a,b,c...y,z组成的字符串S
输出格式:
一个整数表示答案
输入输出样例
aaa
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的更多相关文章
- manacher算法 详解+模板
manacher算法可以解决字符串的回文子串长度问题. 个人感觉szy学长讲的非常好,讲过之后基本上就理解了. 那就讲一下个人的理解.(参考了szy学长的ppt) 如果一个回文子串的长度是偶数,对称轴 ...
- 算法进阶面试题01——KMP算法详解、输出含两次原子串的最短串、判断T1是否包含T2子树、Manacher算法详解、使字符串成为最短回文串
1.KMP算法详解与应用 子序列:可以连续可以不连续. 子数组/串:要连续 暴力方法:逐个位置比对. KMP:让前面的,指导后面. 概念建设: d的最长前缀与最长后缀的匹配长度为3.(前缀不能到最后一 ...
- Manacher算法详解
问题 什么是回文串,如果一个字符串正着度读和反着读是一样的,这个字符串就被称为回文串. such as noon level aaa bbb 既然有了回文,那就要有关于回文的问题,于是就有了-- 最长 ...
- 经典算法 Manacher算法详解
内容: 1.原始问题 =>O(N^2) 2.Manacher算法 =>O(N) 1.原始问题 Manacher算法是由题目“求字符串中长回文子串的长度”而来.比如 abcdcb 的 ...
- [转] Manacher算法详解
转载自: http://blog.csdn.net/dyx404514/article/details/42061017 Manacher算法 算法总结第三弹 manacher算法,前面讲了两个字符串 ...
- hdu3068之manacher算法+详解
最长回文 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submi ...
- KM算法详解+模板
http://www.cnblogs.com/wenruo/p/5264235.html KM算法用来求二分图最大权完美匹配. 本文配合该博文服用更佳:趣写算法系列之--匈牙利算法 现在有N男N女,男 ...
- KM算法 详解+模板
先说KM算法求二分图的最佳匹配思想,再详讲KM的实现.[KM算法求二分图的最佳匹配思想] 对于具有二部划分( V1, V2 )的加权完全二分图,其中 V1= { x1, x2, x3, ... , x ...
- KMP算法 详解+模板
本文大部分摘自szy学长的ppt<string>中的KMP部分. %%%膜拜szy大神orz 1.概述 KMP 算法是用来解决单模匹配问题的一种算法. 如果暴力的进行单模匹配,那么时间复杂 ...
随机推荐
- LeetCode: 59. Spiral Matrix II(Medium)
1. 原题链接 https://leetcode.com/problems/spiral-matrix-ii/description/ 2. 题目要求 给定一个正整数n,求出从1到n平方的螺旋矩阵.例 ...
- C#防止程序重新运行
//禁止重复运行 bool ret; Mutex mutex = new Mutex(true, Application.ProductName, out ret); if (ret) { Appli ...
- journalctl 日志查看方法
1 概述 日志管理工具journalctl是centos7上专有的日志管理工具,该工具是从message这个文件里读取信息.Systemd统一管理所有Unit的启动日志.带来的好处就是,可以只用jo ...
- Restify Api 开发经验
此文已由作者王振华授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 工作期间,一直在用Restify开发或维护大大小小的API系统,现在分享一下一些个人觉得不错的Tips. 充 ...
- MVC下的Area区域知识点
新建area区域 1.如果与根目录下的url相同,那么需要在RouteConfig.cs中 public static void RegisterRoutes(RouteCollection rout ...
- Linux命令应用大词典-第17章 软件包管理
17.1 rpm:RPM软件包管理器 17.2 rpmargs:处理RPM软件包 17.3 rpmbuild:构建RPM软件包 17.4 rpmdiff:比较两个软件包之间的不同 17.5 rpmel ...
- 了解Python控制流语句——if语句
控制流 截止到现在,在我们所看过的程序中,总是有一系列语句从上到下精确排列,并交由 Python 忠实地执行.如果你想改变这一工作流程,应该怎么做?就像这样的情况:你需要程序作出一些决定,并依据不同的 ...
- sqlalchemy 转json 的几种常用方式
sqlalchemy 转json 的几种常用方式 # -*- coding:utf-8 -*- import datetime from flask import Flask, json, jsoni ...
- 在mesh client示例中加入spi_slave接口(without IDE)
在mesh client示例中加入spi_slave接口(without IDE) 主要是理解cmake构建的过程,然后修改工程中的inlcude路径及c源文件. 1. 解压mesh_sdk unzi ...
- jQuery实现仿京东商城图片放大镜
效果图: 不废话直接上代码: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...