【字符串题目讲解】一文理解 Manacher Algoirth(马拉车算法)——以洛谷 P3805 和 P5446 为例

M a n a c h e r   A l g o r i t h m \mathrm{Manacher\ Algorithm} Manacher Algorithm

Manacher 算法主要是解决怎样的问题呢,其实是求解最长的回文串,但是只能找到长度为奇数的回文串,不过可以通过转化使得能够求解任意长度的回文串。

例题:P3805 【模板】manacher

A l g o r i t h m   F l o w \mathrm{Algorithm\ Flow} Algorithm Flow

Manacher 的算法是在 O ( n ) O(n) O(n) 的时间复杂度内,查找对于每一个 i i i,最大的回文半径 p i p_i pi 是多少?

从左往右递推做,在递推的过程中,每一次维护右端点最靠右的回文串的中心位置 m i d mid mid,以及该回文串最靠右的位置 m r mr mr(通常不包含该回文串最靠右的位置,即为该位置 + 1 +1 +1)。(如图)

那么,对于求解 i i i p i p_i pi,可以分情况讨论:

  • i i i m r mr mr 的左边,即在 m i d mid mid 所在回文串的内部。那么,充分利用已经算过的信息,考虑 i i i 在该回文串内的对应点 j j j,其中 j = m i d × 2 − i j=mid\times 2 - i j=mid×2i
  • j j j 所在的最大回文串仍然在 m i d mid mid 所在回文串的范围内,则 p i ≥ p j p_i\ge p_j pipj。(何时取 ≥ \ge ,当且仅当 j j j 的左端点恰好为 m i d mid mid 所在回文串的左端点时)
  • j j j 所在的最大回文串超出了 m i d mid mid 所在回文串的范围,则 p i = m r − i p_i=mr-i pi=mri

综上所述, p i ≥ min ⁡ ( p j , m r − i ) p_i\ge \min(p_j,mr-i) pimin(pj,mri)

  • i i i m r mr mr 的右边,即在 m i d mid mid 所在回文串的外部。那么,此时 p i ≥ 1 p_i\ge 1 pi1

最后,暴力的向外扩展即可:

while (S[i - p[i]] == S[i + p[i]]) p[i] ++;

判断当前的 p i + i p_i+i pi+i 是否超出 m r mr mr,若超出,则更新 m r mr mr m i d mid mid 即可。

A n a l y s i s   o f   t h e   T i m e   C o m p l e x i t y \mathrm{Analysis\ of\ the\ Time\ Complexity} Analysis of the Time Complexity

其实,时间复杂度就是 while 循环的执行次数(即暴力扩展的次数)。

只要执行 while 那么一定会更新 m r mr mr,且 while 执行的次数与 m r mr mr 向右扩展的数量相同。

所以 m r mr mr 总是线性向右递推的,所以总的时间复杂度为 O ( n ) O(n) O(n)

T r a n s f o r m \mathrm{Transform} Transform

刚才的算法只是解决一个字符串中奇数长度的回文串,偶数的是找不出来的。

不过,通过加一些字符可以转化为能够求出任意长度的最长回文串,具体如下:

在任意 2 2 2 个字符之间添加 1 1 1#,首尾也要加入 #,再在首尾分别加入 $^ 以防出界,因为你会发现 while 循环是没有边界的。

具体形式: $ # a 1 # a 2 # a 3 # … a n # ∧ \$ \# a_1\# a_2\# a_3\#\dots a_n\#\wedge $#a1#a2#a3#an#

这样对于新串的最长回文半径 − 1 -1 1 即为旧串最长回文串长度。

A c c e p t e d   C o d e \mathrm{Accepted\ Code} Accepted Code
#include <bits/stdc++.h>
#define int long long

using namespace std;

typedef pair<int, int> PII;
typedef long long LL;

const int SIZE = 4e7 + 10;

int N;
string A, S;
int P[SIZE];

void Manacher()
{
	int mid, mr = 1;
	for (int i = 1; i <= N; i ++)
	{
		if (i < mr) P[i] = min(P[mid * 2 - i], mr - i);
		else P[i] = 1;
		while (S[i - P[i]] == S[i + P[i]]) P[i] ++;
		if (i + P[i] > mr)
		{
			mr = i + P[i];
			mid = i;
		}
	}
}

signed main()
{
	cin.tie(0);
	cout.tie(0);
	ios::sync_with_stdio(0);

	cin >> A;
	N = A.size(), A = ' ' + A;

	S += "$#";
	for (int i = 1; i <= N; i ++)
		S += A[i], S += '#';
	S += '^';
	N = S.size(), S = ' ' + S;

	Manacher();

	int Result = 0;
	for (int i = 1; i <= N; i ++)
		Result = max(Result, P[i] - 1);

	cout << Result << endl;

	return 0;
}

练习题:P5446 【THUPC2018】绿绿和串串

D e s c r i p t i o n \mathrm{Description} Description

定义翻转的操作:把一个串以最后一个字符作对称轴进行翻转复制。形式化地描述就是,如果他翻转的串为 R R R,那么他会将前 R − 1 R-1 R1 个字符倒序排列后,插入到串的最后

现给出字符串 S S S,询问有哪些串长度不超过 ∣ S ∣ |S| S 的串 R R R 经过若干次翻转操作后得到的串作为 T T T 的前缀。

数据范围: ∑ ∣ S ∣ ≤ 5 × 1 0 6 \sum|S|\le 5\times 10^6 S5×106

S o l u t i o n \mathrm{Solution} Solution

考虑怎样的串翻转若干次之后前缀包含 S S S,其一定是 S S S 的一个前缀,这样翻转后 S S S 才会是它的前缀。

那么,对于 S S S 中每一个位置 i i i 分情况讨论:

  • 1 ∼ i 1\sim i 1i 的串翻转 1 1 1 次之后长度超过 ∣ S ∣ |S| S,则要求 i + 1 ∼ ∣ S ∣ i+1\sim |S| i+1S 的串是 1 ∼ i − 1 1\sim i-1 1i1 的串取反后的前缀,即以 i i i 为中心的回文串最大右端点为 ∣ S ∣ |S| S。标记 i i i 位置为可行位置。
  • 1 ∼ i 1\sim i 1i 的串翻转 1 1 1 次之后长度小于 ∣ S ∣ |S| S,则要求 1 ∼ 2 i − 1 1\sim 2i-1 12i1 是回文串,且右端点为可行位置,也就是能够经过多次翻转使 S S S 为其的前缀。

A c c e p t e d   C o d e \mathrm{Accepted\ Code} Accepted Code

#include <bits/stdc++.h>
#define int long long

using namespace std;

typedef pair<int, int> PII;
typedef long long LL;

const int SIZE = 2e6 + 10;

int N;
string S;
int P[SIZE], Vis[SIZE];

void Manacher()
{
	int mr = 1, mid;
	for (int i = 1; i <= N; i ++)
	{
		if (i < mr) P[i] = min(P[mid * 2 - i], mr - i);
		else P[i] = 1;
		while (S[i - P[i]] == S[i + P[i]]) P[i] ++;
		if (P[i] + i > mr)
			mr = P[i] + i, mid = i;
	}
}

void solve()
{
	cin >> S;
	N = S.size(), S = ' ' + S, S += '^';

	Manacher();

	for (int i = N; i >= 1; i --)
		if (i + P[i] - 1 >= N) Vis[i] = 1;
		else if (Vis[i + P[i] - 1] && i - P[i] + 1 == 1) Vis[i] = 1;

	for (int i = 1; i <= N; i ++)
		if (Vis[i])
			cout << i << " ", Vis[i] = 0;
	cout << endl;
}

signed main()
{
	cin.tie(0);
	cout.tie(0);
	ios::sync_with_stdio(0);

	int Data;

	cin >> Data;

	while (Data --)
		solve();

	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/398341.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【9】知识存储

一、图数据库neo4j Neo4j是一个高性能的,NOSQL图形数据库&#xff0c;它将结构化数据存储在网络上而不是表中。它是一个嵌入式的、基于磁盘的、具备完全的事务特性的Java持久化引擎。单节点的服务器可承载上亿级的节点和关系&#xff0c;单节点性能不够时也可进行分布式集群部…

基于bloomz-7b指令微调的中文医疗问诊大模型,实现智能问诊、医疗问答

基于bloomz-7b指令微调的中文医疗问诊大模型&#xff0c;实现智能问诊、医疗问答 码源见文末 1.项目简介 本项目开源了基于医疗指令微调的中文医疗问诊模型&#xff1a;明医 (MING)。目前模型的主要功能如下&#xff1a; 医疗问答&#xff1a;对医疗问题进行解答&#xff0…

python 多线程+队列的简单应用

在实际处理需求的过程中&#xff0c;博主比较偏爱使用多线程threading 队列queue的方式 去开发代码。 &#xff08;本文注重的是搭建模板框架&#xff0c;仅供参考&#xff09; 举例&#xff1a; 以豆瓣电影的排行为例&#xff0c;写个简单的demo。 豆瓣链接&#xff1a;ht…

基于用Python对电商销售数据分析展示项目

项目背景 假设您有一个电商网站的销售数据集&#xff0c;包含用户购买记录、产品信息和销售时间等信息。您希望通过数据分析来找出哪些产品销售最好&#xff0c;以及哪些用户群体对哪些产品更感兴趣。 目录 项目背景 项目流程 数据收集和导入 数据集示例&#xff08;CSV格式…

IO 作业 24/2/20

一、思维导图 二、习题 #include <myhead.h> int main(int argc, const char *argv[]) {FILE *fpNULL;FILE *fqNULL;pid_t pidfork();if(pid>0){if((fpfopen("./text.txt","r"))NULL){perror("fopen error");return -1;} if((f…

【2024软件测试面试必会技能】Jmeter_性能测试(3):性能测试脚本的制作和调试

Charles Jmeter的性能测试脚本的制作和调试 以PHP论坛为例&#xff1a;http://47.107.178.45/phpwind/ Charles抓包 1、charles设置过滤&#xff1b;可参考&#xff1a;https://www.cnblogs.com/YouJeffrey/p/15334939.html 2、对于抓包操作进行备注 3、去掉资源文件&…

AS-V1000 视频监控平台产品介绍:客户端功能介绍(四)

目 录 一、引言 1.1 AS-V1000视频监控平台介绍 1.2平台服务器配置说明 二、软件概述 2.1 客户端软件用途 2.2 客户端功能 三、客户端功能说明 3.1告警管理 3.1.1告警联动 &#xff08;1&#xff09;告警联动显示 &#xff08;2&#xff09;告警联动处理 3…

Packet Tracer - 配置 IPv4 和 IPv6 接口

地址分配表 目标 第 1 部分&#xff1a;配置 IPv4 编址并验证连接第 2 部分&#xff1a;配置 IPv6 编址并验证连接背景信息 路由器 R1 和 R2 分别有两个 LAN。 您的任务是在每台设备上配置合适的编址并验证 LAN 之间的连接。 注&#xff1a;用户 EXEC 密码是 cisco。 特权 E…

单片机学习笔记---红外遥控红外遥控电机调速(完结篇)

目录 低电平触发中断和下降沿触发中断的区别 红外遥控 Int0.c Int.h Timer0.c Timer0.h IR.c IR.h main.c 红外遥控电机调速 Timer1.c Timer.h Motor.c Motor.h main.c 上一节讲了红外发送和接收的工作原理&#xff0c;这一节开始代码演示&#xff01; 提前说…

软件安装遇到bug、报错不知道怎么解决?赶紧收藏起来!

前言 本文举例了几个常见的软件工具使用问题&#xff0c;文末会提供一些我自己整理和使用的工具资料 。 "在追逐零 Bug 的路上&#xff0c;我们不断学习、改进&#xff0c;更加坚定自己的技术信念。让我们相信&#xff0c;每一个 Bug 都是我们成长的机会。" 一、VM…

Microsoft Visio 摄像机图标

Microsoft Visio 摄像机图标 1. 更多形状 -> 搜索形状2. 摄像机References 1. 更多形状 -> 搜索形状 2. 摄像机 ​​​ References [1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/

2024云服务器ECS新老用户优惠价格表,收费更新

2024年阿里云服务器租用价格表更新&#xff0c;云服务器ECS经济型e实例2核2G、3M固定带宽99元一年、ECS u1实例2核4G、5M固定带宽、80G ESSD Entry盘优惠价格199元一年&#xff0c;轻量应用服务器2核2G3M带宽轻量服务器一年61元、2核4G4M带宽轻量服务器一年165元12个月、2核4G服…

h5页面实现图片局部热区跳转

之前没有了解过图片局部热区跳转这种样式&#xff0c;百度搜索实现方法发现两种方法。所谓图片热区跳转就是用户点击图片中的某些区域可以跳转到不同的页面&#xff0c;如下图&#xff0c;点击“舞动人生馆”可以跳转到舞动人生对应的活动页面&#xff0c;点击展望美好馆可以跳…

【嵌入式学习】QT-Day1-Qt基础

笔记 https://lingjun.life/wiki/EmbeddedNote/20QT 毛玻璃登录界面实现&#xff1a;

Android的Compose

Jetpack Compose 是用于构建原生 Android 界面的新工具包&#xff0c;无需修改任何 XML 布局&#xff0c;也不需要使用布局编辑器。相反&#xff0c;只需调用可组合函数来定义所需的元素&#xff0c;Compose 编译器即会完成后面的所有工作。 简而言之&#xff0c;使用Compose&…

【论文精读】ESViT

摘要 基于transformer的SSL方法在ImageNet线性检测任务上取得了最先进的性能&#xff0c;其关键原因在于使用了基于对比学习方法训练单尺度Transformer架构。尽管其简单有效&#xff0c;但现有的基于transformer的SSL&#xff08;自监督学习&#xff09;方法需要大量的计算资源…

Laravel02 路由基本概念和用法 给视图传递请求参数

Laravel02 路由基本概念和用法 1. 路由的基本概念2. 给视图传递请求参数 1. 路由的基本概念 routes文件夹下的web.php是用来定义路由规则的。 自己定义一个路径 2. 给视图传递请求参数 在laravel里使用一个辅助函数request来快速获取请求参数

[ansible] playbook角色

一、roles Roles又称为角色&#xff0c;playbook被称为剧本。Roles角色是自1.2版本之后引入的新特性&#xff0c;用于层次性、结构化的组织剧本 roles能够根据层次型结构自动装载变量文件、任务集、以及触发的动作等&#xff0c;要使用roles只需要在剧本中使用include命令引入…

五种多目标优化算法(MOAHA、MOGWO、NSWOA、MOPSO、NSGA2)性能对比,包含6种评价指标,9个测试函数(提供MATLAB代码)

一、5种多目标优化算法简介 1.1MOAHA 1.2MOGWO 1.3NSWOA 1.4MOPSO 1.5NSGA2 二、5种多目标优化算法性能对比 为了测试5种算法的性能将其求解9个多目标测试函数&#xff08;zdt1、zdt2 、zdt3、 zdt4、 zdt6 、Schaffer、 Kursawe 、Viennet2、 Viennet3&#xff09;&#xff…

计算机网络看这里就够了!!!

入门概念 参考视频链接&#xff1a; 1.2 因特网概述_哔哩哔哩_bilibili 一些基础概念 因特网发展阶段&#xff1a; 三个大标题&#xff1a; 从单个ARPANET-----逐步建成三级结构的因特网----逐步形成多层次ISP结构(互联网服务提供商&#xff08;Internet Service Provider…