算法用处:

解决最长回文子串的问题(朴素型)。


算法复杂度

我们不妨先看看其他暴力解法的复杂度:

  • \(O(n^3)\)

    枚举子串的左右边界,然后再暴力判断是否回文,对答案取 \(max\) 。
  • \(O(n^2)\)

    枚举回文子串的对称轴,向两边扩展,对答案取 \(max\) 。
  • \(O(n)\)

    \(\texttt{Manacher}\) 算法。

显然我们的 \(\texttt{Manacher}\) 是十分优秀的。。。


实现原理

\(\text{step 1}\)

首先我们需解决一个问题:

回文串的长度有有奇也有偶,这会对我们的算法带来不利,如何解决?

我们可以对原串进行一些改造:

我们在原串的每两个字符之间都插入同一个字符(保证与原来串中的字符不重复)。

举个例子:

对于一个串:\(S = \texttt{"abbabaab"}\)

我们进行上述操作:\(S^\prime = \texttt{"/a/b/b/a/b/a/a/b/"}\)

我们把它变成这样,于是我们发现:

对于长度为奇数的回文串,它的回文中心是原串的字符;

对于长度为偶数的回文串,它的回文中心是加入的字符;

那么我们 \(S\) 中的任意一个回文子串都会在 \(S^\prime\) 中唯一对应一个字符。

于是我们可以开始在 \(S^\prime\) 上进行操作。

\(\text{step 2}\)

我们对于 \(S^\prime\) 中的每一个字符 \(S^\prime[i]\) 维护一个 \(f[i]\)

表示以 \(S^\prime[i]\) 为回文中心可以扩展的最远位置和 \(i\) 的下标之差(也就是这两点之间的距离)

我们只要求出了每一个位置的 \(f[i]\),那么该位置作为对称中心时的回文串的长度就是 \(f[i]\):

  • 对于 \(S^\prime[i] \in S\),(比如 /q/w/q/),显而易见。。。
  • 对于 \(S^\prime[i] \notin S\),(比如 /y/y/),这也是显而易见的。。。

于是我们考虑求出这个 \(f[i]\),答案就是 \(\max\limits_{1\leq i \leq N}\{f[i]\}\)


\(\text{step 3}\)

我们需要发现一个很重要的性质:

  • 如果对于一个回文子串,其内部还有一个回文子串,
  • 那么也必定会有一个与之全等的字串与它关于这个较大回文子串的对称中心对称

这个性质应该是显然的。。。

在整个算法进行过程中,我们记录一个 \(MaxRight\) 和一个 \(Mid\)

其中 \(MaxRight\) 表示我们扩展过的最远的位置,\(Mid\) 表示它对应的回文中心。

那么对于我们当前的 \(i\) :

  • 若 \(i<MaxRight\) ,那么我们可以把 \(f[i]\) 初始化为 \(\min\{f[j], MaxRight-i\}\)

    其中的 \(j\) 表示我们刚刚提到过的,该位置对称的那个点,

    即以 \(i\) 为对称中心的回文子串与以 \(j\) 为对称中心的回文子串关于 \(Mid\) 对称。
  • 若 \(i>MaxRight\) ,那么就不管。。。

然后,我们暴力的去扩展 \(f[i]\),并最终用固定下来的 \(i\) 和 \(f[i]\) 来更新 \(MaxRight\) 和 \(Mid\)。

这样就可以在 \(O(n)\) 时间内求出所有的 \(f[i]\) 了。(复杂度证明?我好像不会。。。)


模板题

Luogu

板子题不解释

/*--------------------------------
--Author: The Ace Bee-------------
--Blog: www.cnblogs.com/zsbzsb----
--This code is made by The Ace Bee
--------------------------------*/
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cctype>
#include <cmath>
#include <ctime>
#define rg register
#define clr(x, y) memset(x, y, sizeof x)
using namespace std;
template < typename T > inline void read(T& s) {
s = 0; int f = 0; char c = getchar();
while (!isdigit(c)) f |= (c == '-'), c = getchar();
while (isdigit(c)) s = s * 10 + (c ^ 48), c = getchar();
s = f ? -s : s;
} const int _(11000010); int n, f[_ << 1];
char s[_ << 1], tmp[_]; inline int manacher() {
int n = strlen(s + 1);
for (rg int i = 1; i <= n; ++i) tmp[i] = s[i];
for (rg int i = 1, j = 0; i <= n; ++i) s[++j] = '`', s[++j] = tmp[i];
s[0] = s[n * 2 + 1] = '`', n = n * 2 + 1;
int mx = 0, mid;//mx 即 MaxRight,mid 即 Mid
for (rg int i = 1; i <= n; ++i) {
f[i] = i < mx ? min(mx - i, f[mid * 2 - i]) : 0;
while (s[i - f[i] - 1] == s[i + f[i] + 1]) ++f[i];
if (i + f[i] > mx) mx = i + f[i], mid = i;
}
int res = 0;
for (rg int i = 1; i <= n; ++i) res = max(res, f[i]);
return res;
} int main() {
#ifndef ONLINE_JUDGE
freopen("in.in", "r", stdin);
#endif
scanf("%s", s + 1);
printf("%d\n", manacher());
return 0;
}

Manacher 算法学习笔记的更多相关文章

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

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

  2. Manacher算法学习笔记

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

  3. C / C++算法学习笔记(8)-SHELL排序

    原始地址:C / C++算法学习笔记(8)-SHELL排序 基本思想 先取一个小于n的整数d1作为第一个增量(gap),把文件的全部记录分成d1个组.所有距离为dl的倍数的记录放在同一个组中.先在各组 ...

  4. Johnson算法学习笔记

    \(Johnson\)算法学习笔记. 在最短路的学习中,我们曾学习了三种最短路的算法,\(Bellman-Ford\)算法及其队列优化\(SPFA\)算法,\(Dijkstra\)算法.这些算法可以快 ...

  5. 某科学的PID算法学习笔记

    最近,在某社团的要求下,自学了PID算法.学完后,深切地感受到PID算法之强大.PID算法应用广泛,比如加热器.平衡车.无人机等等,是自动控制理论中比较容易理解但十分重要的算法. 下面是博主学习过程中 ...

  6. Johnson 全源最短路径算法学习笔记

    Johnson 全源最短路径算法学习笔记 如果你希望得到带互动的极简文字体验,请点这里 我们来学习johnson Johnson 算法是一种在边加权有向图中找到所有顶点对之间最短路径的方法.它允许一些 ...

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

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

  8. 算法学习笔记——sort 和 qsort 提供的快速排序

    这里存放的是笔者在学习算法和数据结构时相关的学习笔记,记录了笔者通过网络和书籍资料中学习到的知识点和技巧,在供自己学习和反思的同时为有需要的人提供一定的思路和帮助. 从排序开始 基本的排序算法包括冒泡 ...

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

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

随机推荐

  1. socketserver模块(实现并发)

    socketserver模块(实现并发) 一.基于UDP协议实现的并发 # 服务端 import socketserver class MyServer(socketserver.BaseReques ...

  2. 如何在app.js 和其他页面中更改globalData的值

    它不能用this.setData方法更改值,该方法只能更改data:{}对象(而且在app.js中无法使用该方法),因此用app.globalData.isLogin = true;

  3. 巧用命令行工具 redis-cli

    我们天天都在使用 Redis 内置的命令行工具 redis-cli,久而久之以为它就是一个简单的交互式 Redis 数据结构手工操作程序,但是它背后强大的功能绝大多数同学可能闻所未闻.本节我们一起来挖 ...

  4. HDU 1312 Red and Black(经典DFS)

    嗯... 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1312 一道很经典的dfs,设置上下左右四个方向,读入时记下起点,然后跑dfs即可...最后答 ...

  5. Shiro入门学习之散列算法与凭证配置(六)

    一.散列算法概述 散列算法一般用于生成数据的摘要信息,是一种不可逆的算法,一般适合存储密码之类的数据,常见的散列算法如MD5.SHA等,一般进行散列时最好提供一个salt(“盐”),什么意思?举个栗子 ...

  6. centos610无桌面安装JDK

     Centos610系列配置 1.使用yum查找jdk: yum search java|grep jdk    2.选择安装截图中选中的版本 yum install java-1.8.0-openj ...

  7. [linux] ARCH LINUX 常见问题及实用工具汇总

    1.办公 screen-recorder 1)深度linux桌面录制软件(不支持录音),操作简单,可以保存mp4和gif格式 2)命令安装:sudo pacman -S deepin-screen-r ...

  8. 基于SSH开发银行个人业务管理系统 附源码

    开发环境: Windows操作系统开发工具: MyEclipse+Jdk+Tomcat+MySql数据库 运行效果图

  9. Java常用API——Arrays工具类

    介绍:Arrays工具类提供了一些可以直接操作数组的方法,以下是一些常用方法: int binarySearch(type[] a, type key):要求数组a元素升序排列,使用二分法搜索key的 ...

  10. matplotlib显示AttributeError: 'module' object has no attribute 'verbose'

    解决办法:file-settings-tools-python scientific,将show plots in toolwindow前面的对号去掉即可.