Manacher 算法学习小记
概要
一个字符串有多少个回文的字串?最多有 \(O(n^2)\) 级别个。但 Manacher 算法却可以用 \(O(n)\) 的时间复杂度解决这个问题。同时 Manacher 算法实现非常简单。
一个显而易见的结论是:如果 \(S_{1\cdots n}\) 是回文串,那么 \(S_{2\cdots n-1}\) 也是回文串。
根据这一个性质,我们可以得到 \(O(n^2)\) 的暴力的做法:
以 \(i\) 为中心,向两侧暴力扩展,得到所有以 \(i\) 位中心的回文串。这些回文串长度为奇数。
以 \(i\) 和 \(i+1\) 为中心,向两侧暴力扩展,得到所有以 \(i\) 和 \(i+1\) 为中心的回文串。这些回文串的长度为偶数。
实际上,为了方便实现,可以在两个字符间和首尾插入空字符。这样所有的回文串的长度都变为奇数。下面默认使用了这种方法,所有回文串长度为奇数,下标从 \(1\) 开始。
Manacher 充分利用了回文的性质,构造出令人惊叹的巧妙做法:
令 \(d_i\) 表示以 \(i\) 为中心的回文串最大长度的一半,那么只要求得 \(d\) ,就可以知道所有回文串的信息。
不妨令 \(l,r\) 表示当前考虑到的回文串中 右端点最靠右 的那个回文串。初始时不妨令 \(l=r=0\) 。
从左到右枚举回文中心 \(i\) 。如果 \(i > r\) ,那么调用暴力算法求得 \(d_i\) 。否则可以找到回文串 \(S_{l\cdots r}\) 中与 \(i\) 对称的位置 \(j=l+(r-i)\) 。此时,如果 \(i+d_j < r\) ,那么根据 \(S_{l\cdots r}\) 的对称性, \(d_i=d_j\) 。否则的话, 由于无法保证 \(r\) 之后与 \(l\) 之前的对称性,先令 \(d_i=r-i\) ,再在此基础上执行暴力算法。
最后不要忘记更新 \(l,r\) 。
不难发现, \(r\) 是不减的。而暴力算法中向两侧暴力拓展的次数不超过 \(r\) 增加的值。所以 Manacher 算法中,暴力的部分均摊是 \(O(n)\) 的。外层循环也是 \(O(n)\) ,那么总的时间复杂度就是 \(O(n)\) 的。
例
\(S\) 长度 \(1e5\) 我线段树一只 \(log\) T 了?这个评测机略微有点快啊……不过可以 \(O(n)\) 的……
在 Manacher 之后,可以 \(O(n)\) 预处理出对于每个位置 \(i\) 为结尾的最长回文串和以 \(i\) 为开始的最长回文串。通过加入的空字符统计答案即可。注意必须要是两个回文串,所以单一一个空字符不能算作回文。
#include <cstdio>
#include <cstring>
#include <algorithm>
const int Maxn = 100010;
const int INF = 1e9;
char Ch[Maxn << 1];
int D[Maxn << 1];
int n, Ans, L[Maxn << 1], R[Maxn << 1];
inline void Manacher();
int main() {
scanf("%s", Ch + 1);
n = strlen(Ch + 1);
for (int i = n; i >= 1; --i) Ch[i << 1] = Ch[i];
for (int i = 0; i <= n; ++i) Ch[i << 1 | 1] = '_';
n = n << 1 | 1;
Ch[0] = '*', Ch[n + 1] = '\0';
Manacher();
for (int i = 1; i <= n; ++i) {
L[i + D[i]] = std::max(L[i + D[i]], D[i]);
R[i - D[i]] = std::max(R[i - D[i]], D[i]);
}
for (int i = 1; i <= n; ++i)
if (i & 1)
R[i] = std::max(R[i], R[i - 2] - 2);
for (int i = n; i >= 1; --i)
if (i & 1)
L[i] = std::max(L[i], L[i + 2] - 2);
Ans = 0;
for (int i = 1; i <= n; ++i)
if (R[i] && L[i])
Ans = std::max(Ans, R[i] + L[i]);
printf("%d\n", Ans);
return 0;
}
inline void Spand(int x) {
for(; Ch[x - D[x] - 1] == Ch[x + D[x] + 1]; ++D[x]);
return;
}
inline void Manacher() {
int L = 0, R = 0;
for (int i = 1; i <= n; ++i) {
if (i > R) {
Spand(i);
L = i - D[i], R = i + D[i];
continue;
}
int Ops = L + (R - i);
if (i + D[Ops] >= R) {
D[i] = R - i;
Spand(i);
L = i - D[i], R = i + D[i];
continue;
}
D[i] = D[Ops];
}
return;
}
Manacher 算法学习小记的更多相关文章
- Manacher算法学习笔记 | LeetCode#5
Manacher算法学习笔记 DECLARATION 引用来源:https://www.cnblogs.com/grandyang/p/4475985.html CONTENT 用途:寻找一个字符串的 ...
- Gcd&Exgcd算法学习小记
Preface 对于许多数论问题,都需要涉及到Gcd,求解Gcd,常常使用欧几里得算法,以前也只是背下来,没有真正了解并证明过. 对于许多求解问题,可以列出贝祖方程:ax+by=Gcd(a,b),用E ...
- Manacher算法学习 【马拉车】
好久没写算法学习博客了 比较懒,一直在刷水题 今天学一个用于回文串计算问题manacher算法[马拉车] 回文串 回文串:指的是以字符串中心为轴,两边字符关于该轴对称的字符串 ——例如abaaba 最 ...
- manacher算法学习(求最长回文子串长度)
Manacher总结 我的代码 学习:yyb luogu题目模板 xzy的模板 #include<iostream> #include<cstdlib> #include< ...
- Manacher算法学习笔记
前言 Manacher(也叫马拉车)是一种用于在线性时间内找出字符串中最长回文子串的算法 算法 一般的查找回文串的算法是枚举中心,然后往两侧拓展,看最多拓展出多远.最坏情况下$O(n^2)$ 然而Ma ...
- Manacher 算法学习笔记
算法用处: 解决最长回文子串的问题(朴素型). 算法复杂度 我们不妨先看看其他暴力解法的复杂度: \(O(n^3)\) 枚举子串的左右边界,然后再暴力判断是否回文,对答案取 \(max\) . \(O ...
- Cipolla算法学习小记
转自:http://blog.csdn.net/doyouseeman/article/details/52033204 简介 Cipolla算法是解决二次剩余强有力的工具,一个脑洞大开的算法. 认真 ...
- 学习笔记 - Manacher算法
Manacher算法 - 学习笔记 是从最近Codeforces的一场比赛了解到这个算法的~ 非常新奇,毕竟是第一次听说 \(O(n)\) 的回文串算法 我在 vjudge 上开了一个[练习],有兴趣 ...
- 二次剩余Cipolla算法学习笔记
对于同余式 \[x^2 \equiv n \pmod p\] 若对于给定的\(n, P\),存在\(x\)满足上面的式子,则乘\(n\)在模\(p\)意义下是二次剩余,否则为非二次剩余 我们需要计算的 ...
随机推荐
- Idea 快捷生成方法(待完善)
1.System.out.println() 输入sout,按下enter键,生成System.out.println()方法. sout--->soutv=System.out.println ...
- php 处理数字为金钱格式
number_format(需要转换的数字,保留小数个数,小数点符号,每三位的分隔符) echo number_format("1000000")."<br> ...
- JAVA学习篇--静态代理VS动态代理
本篇博客的由来,之前我们学习大话设计,就了解了代理模式,但为什么还要说呢? 原因: 1,通过DRP这个项目,了解到了动态代理,认识到我们之前一直使用的都是静态代理,那么动态代理又有什么好处呢?它们二者 ...
- VBA精彩代码分享-1
今天下班前分享一下之前在网上搜到的两段好用的VBA代码,貌似都来自国外,觉得挺好,模仿不来. 第一段的功能是修改VBA控件中的文本框控件,使其右键可以选择粘贴.复制.剪切等: Option Expli ...
- Swagger学习(四、配置API文档的分组)
完整示例 代码结构 运行效果 SwaggerConfig.class @Configuration //变成配置文件 @EnableSwagger2 //开启swagger2 public class ...
- ADO与达梦7产生的一个未知问题
采用OLEDB与达梦7建立数据库连接 连接成功 查询表成功 打开表成功 当进行到addnew 操作时 报异常,未知错误 而且是仅针对这张表 ,其他表都没有问题 当清空数据后可以再插入一次数据,之后就 ...
- 【Android】二、HelloWorld
1. 按照该网址写HelloWorld 例子 http://www.runoob.com/android/android-hello-world-example.html 2.点击 make pr ...
- NPOI 实现在已存在的Excel中任意位置开始插入任意数量行,并填充数据
1 npoi版本2.1.3.1 2 需要添加的引用: using NPOI.SS.UserModel;using NPOI.XSSF.UserModel;using System.IO;using N ...
- Vue.prototype详解
参考地址:Vue.prototype详解 如果需要设置 全局变量,在main.js中,Vue实例化的代码里添加. 不想污染全局作用域.这种情况下,你可以通过在 原型 上定义它们使其在每个Vue实例中可 ...
- Image Processing and Analysis_8_Edge Detection:The Design and Use of Steerable Filters——1991
此主要讨论图像处理与分析.虽然计算机视觉部分的有些内容比如特 征提取等也可以归结到图像分析中来,但鉴于它们与计算机视觉的紧密联系,以 及它们的出处,没有把它们纳入到图像处理与分析中来.同样,这里面也有 ...