概要

一个字符串有多少个回文的字串?最多有 \(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)\) 的。

luogu4555

\(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 算法学习小记的更多相关文章

  1. Manacher算法学习笔记 | LeetCode#5

    Manacher算法学习笔记 DECLARATION 引用来源:https://www.cnblogs.com/grandyang/p/4475985.html CONTENT 用途:寻找一个字符串的 ...

  2. Gcd&Exgcd算法学习小记

    Preface 对于许多数论问题,都需要涉及到Gcd,求解Gcd,常常使用欧几里得算法,以前也只是背下来,没有真正了解并证明过. 对于许多求解问题,可以列出贝祖方程:ax+by=Gcd(a,b),用E ...

  3. Manacher算法学习 【马拉车】

    好久没写算法学习博客了 比较懒,一直在刷水题 今天学一个用于回文串计算问题manacher算法[马拉车] 回文串 回文串:指的是以字符串中心为轴,两边字符关于该轴对称的字符串 ——例如abaaba 最 ...

  4. manacher算法学习(求最长回文子串长度)

    Manacher总结 我的代码 学习:yyb luogu题目模板 xzy的模板 #include<iostream> #include<cstdlib> #include< ...

  5. Manacher算法学习笔记

    前言 Manacher(也叫马拉车)是一种用于在线性时间内找出字符串中最长回文子串的算法 算法 一般的查找回文串的算法是枚举中心,然后往两侧拓展,看最多拓展出多远.最坏情况下$O(n^2)$ 然而Ma ...

  6. Manacher 算法学习笔记

    算法用处: 解决最长回文子串的问题(朴素型). 算法复杂度 我们不妨先看看其他暴力解法的复杂度: \(O(n^3)\) 枚举子串的左右边界,然后再暴力判断是否回文,对答案取 \(max\) . \(O ...

  7. Cipolla算法学习小记

    转自:http://blog.csdn.net/doyouseeman/article/details/52033204 简介 Cipolla算法是解决二次剩余强有力的工具,一个脑洞大开的算法. 认真 ...

  8. 学习笔记 - Manacher算法

    Manacher算法 - 学习笔记 是从最近Codeforces的一场比赛了解到这个算法的~ 非常新奇,毕竟是第一次听说 \(O(n)\) 的回文串算法 我在 vjudge 上开了一个[练习],有兴趣 ...

  9. 二次剩余Cipolla算法学习笔记

    对于同余式 \[x^2 \equiv n \pmod p\] 若对于给定的\(n, P\),存在\(x\)满足上面的式子,则乘\(n\)在模\(p\)意义下是二次剩余,否则为非二次剩余 我们需要计算的 ...

随机推荐

  1. java项目上线的流程(将web项目部署到公网)

    本博文来源于网络,原文的地址在本篇博文最下方. 如何将java web项目上线/部署到公网 关于如何将Java Web上线,部署到公网,让全世界的人都可以访问的问题.小编将作出系列化,完整的流程介绍. ...

  2. Python 常用内置模块详解

    Python 的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程序,作为ABC语言的一种继承.Py ...

  3. IOC+EF+Core项目搭建IOC注入及框架(二)

    配置ServiceCollection /// <summary> /// 表示IServiceCollection的扩展 /// </summary> public stat ...

  4. 1 java 笔记

    第一java的版本: J2ME主要用于移动设备和信息家电 J2SE整个Java技术的核心 J2EE java技术应用最广泛的部分,主要应用与企业的开发 第二:基于java语言的开源框架 struts ...

  5. 如何远程调试部署在CloudFoundry平台上的nodejs应用

    网络上关于如何本地调试nodejs应用的教程已经很多了,工具有Chrome开发者工具,Visual Studio Code,和nodejs周边的一些小工具等等. 在实际情况中,我们可能遇到本地运行良好 ...

  6. Golang之初探

    什么是Go语言 Go语言介绍 产生背景: 超级复杂的C++11特性的吹捧报告的鄙视以及最终的目标是具备动态语言的开发速度的同时并要有C/C++编译语言的性能与安全性以及设计网络和多核时代的C语言 Go ...

  7. VirtualBox虚拟CentOS7共享文件夹

    在VirtualBox的centos虚拟机光盘设置为安装增强工具包VBoxGuestAdditions.iso 进入centos, cd /media mkdir cdrom mount -t iso ...

  8. Git修改已经提交的用户名信息

    由于工作或者其他原因,有时候我们会修改git的用户名和邮箱账号,没有改过来就提交,就会导致提交人信息不一致的问题.现在记录修正回来的方法 # 第一步,(n)代表提交次数 git rebase -i H ...

  9. PAT Basic 1072 开学寄语 (20 分)

      下图是上海某校的新学期开学寄语:天将降大任于斯人也,必先删其微博,卸其 QQ,封其电脑,夺其手机,收其 ipad,断其 wifi,使其百无聊赖,然后,净面.理发.整衣,然后思过.读书.锻炼.明智. ...

  10. Java查漏补缺

    1.自动转换按从低到高的顺序转换.不同类型数据间的优先关系如下: 低 ---------------------------------------------> 高 byte,short,ch ...