比KMP简单的Manacher

news2025/1/2 2:58:25

P3805 【模板】manacher - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

“没时间悼念KMP了,接下来上场的是Manacher!”


什么是Manacher?

历史背景:

1975 年,一个叫 Manacher 的人发明了这个算法,所以叫Manacher 算法(中文名:马拉车算法)

应用背景:

现在有个字符串A,你需要找到该字符串中最长的回文字串,输出其长度。

回文串是正着读反着读都是一样的,例如:101,aba,tnt。

Manacher算法思路:

Manacher第一步修改原字符串。

我们认定一个字符串是否为回文串,都会先找这个字符串的中心,然后向两边扩散进行字符对比。比如aba这个字符串,我们要判断它是否为回文串,就可以先找到中心位置b,然后再向两边扩散比较,b左边的字符是不是等于b右边的字符,如果都一致,那么该字符串就是回文串。

当然,聪明的你一定发现了一个问题,如果该字符串是abba这种偶数长度的字符串呢?这个时候我们就可以把偶数长度的字符串变为奇数长度的字符串,向原字符串插入一个原字符串不会出现的字符,比如,"$"。那么对于abba而言插入后的字符串就变成了$a$b$b$a$。然后我们就可以套用判断奇字符串是否是回文串的思想,来对这个进行判断了。

插入相同字符后的字符串如果是回文串,那么没有插入相同字符的字符串也是回文串,可自行验证。

所以Mannacher的第一步就是在原来的字符串的基础上,插入一个原字符串不会出现的字符。

Manacher第二步回文范围[L,R],回文中心W。

约定p数组放的是回文半径,则p[i]存放的是以i为回文中心的回文半径。

如图,在字符串(黑色框)中有一回文子串(蓝色框),范围为[L, R],其中M为该回文串的中心,现在我们欲求以i为回文中心的回文半径r。就要先分两种大情况。

第一种是i在[L, R]区间里,我们就可以先找i相当于M的对称点K,因为i是在K之后的所以K的回文半径是已知的,那么当K的回文区域不包含L,即K-p[K]+1>L时,p[i] = p[k] =[2*M-i]。当K的回文区域包含L时,即K-p[K]+1<=L时,p[i] = R-i+1。

第二种是i在[L, R]区间外面的,我们就只能暴力向两边扩散比较,求得它的回文半径。

经过如图的数学推导,我们就可以给第一种情况化简为p[i]=min(p[2*M-i], R-i+1),然后再向两边扩散比较字符。

通过以上这种方法,写一个for循环加一层while就能够快捷地得到以每个字符作为回文中心的回文半径了。

得到回文半径有什么用呢?题目不是让我们求字符串里面最长的回文子串有多长嘛?那么最大的回文半径-1,不就是最长的回文子串的长度嘛?也就解开了。

因为原字符串在插入了原字符串没有的字符后,原字符串的长度变成了原来的2倍,所以回文半径-1就是最长的回文子串。可自己写几个试试。

【算法详解】Manacher算法_哔哩哔哩_bilibili 


如何实现Manacher代码?

插入原字符串没有的字符

for (ll i = 0; i < 2 * a.length(); i++)
	{
		if (i & 1)
//如果是奇数
			b[i] = a[i / 2];

		else
			b[i] = '$';
	}
	b[2 * a.length()] = '$';
//a为原字符串
//b为插入后的字符串

求以每个字符作为回文中心的回文半径

ll M = 0, R = 0;
//初始化M和R
	for (ll i = 0; i <= 2 * a.length(); i++)
	{
// for循环遍历字符串里面每个字符
		if (i > R)
			p[i] = 1;
//如果i是在[L, R]区间外面的 
//那么它的回文半径默认从1开始
		else
			p[i] = min(p[2 * M - i], R - i + 1);
//否则回文半径就是min(p[2 * M - i], R - i + 1)
		while (i + p[i]<= 2 * a.length() && i - p[i] >= 0 && b[i - p[i]] == b[i + p[i]])
//while循环就是向两边扩散比较的过程
			p[i]++;
//只有当左右两边的字符一致
//回文半径才+1
		if (i + p[i] - 1 > R)
		{
			M = i;
			R = i + p[i] - 1;
		}
//如果以新的字符为回文中心
//回文半径更大的话
//那么就以新的字符为回文中心
//更新R
	}

全部代码

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll N = 2e7;
string a;
vector<ll> p(2 * N);
char b[2 * N];

int main()
{
	cin >> a;
	for (ll i = 0; i < 2 * a.length(); i++)
	{
		if (i & 1)
			b[i] = a[i / 2];

		else
			b[i] = '$';
	}
	b[2 * a.length()] = '$';
	// cout << "变形后的:" << b << endl;
	ll M = 0, R = 0;
	for (ll i = 0; i <= 2 * a.length(); i++)
	{
		if (i > R)
			p[i] = 1;
		else
			p[i] = min(p[2 * M - i], R - i + 1);
		while (i + p[i]<= 2 * a.length() && i - p[i] >= 0 && b[i - p[i]] == b[i + p[i]])
			p[i]++;
		if (i + p[i] - 1 > R)
		{
			M = i;
			R = i + p[i] - 1;
		}
	}
	// cout << M << " " << R << endl;

	cout << *max_element(p.begin(), p.end()) - 1;

	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

npm镜像源证书过期问题解决

title: npm镜像源证书过期 search: 2024-02-29 文章目录 Failed to check for updates 问题ERR_PNPM_NO_PKG_MANIFESTnpm缓存清除指令权限不足导致删除不了解决方案npm创建基础配资文件 Failed to check for updates 问题 错误描述如上 检查完 node,vue,npm 的版本后都没啥问…

瑞吉外卖实战学习--8、人员禁用和启用

前言 1、通过前端页面查看接口 会发现请求方式是put 请求接口是employee 2、检查页面传值 根据浏览器的请求可以看到传值为id和status 2、写put请求&#xff0c;添加修改时间和修改人的id然后传回给后台 /*** 启用和禁用员工账号* param request* param employee* return…

Linux:ip协议

文章目录 ip协议基本认识ip协议的报头 ip协议基本认识 前面对于TCP的内容已经基本结束了&#xff0c;那么这也就意味着在传输层也已经结束了&#xff0c;那么下一步要进入的是的是网络层&#xff0c;网络层中也有很多种协议&#xff0c;这里主要进行解析的是ip协议 前面的TCP…

【应用笔记】LAT1413+快速开关蓝牙导致设备无广播

1. 问题背景 客户使用 BlueNRG-345MC 开发了一个 BLE 外设&#xff0c;和手机连接。在测试中发现&#xff0c;手机连接上外设之后&#xff0c;不断地在手机上点击蓝牙的开关按钮&#xff0c;造成设备不断地断开、重连&#xff1b;少则几次&#xff0c;多则几十次。点击之后&am…

【前端面试3+1】07vue2和vue3的区别、vue3响应原理及为什么使用proxy、vue的生命周期中在什么时期给接口发请求、【找出数组最大公约数】

一、vue2和vue3的区别 1.性能优化&#xff1a; Vue 3在性能方面有很大的提升&#xff0c;主要是通过虚拟DOM的优化和响应式系统的改进实现的。 虚拟 DOM 重构&#xff1a;Vue 3 中对虚拟 DOM 进行了重构&#xff0c;使得更新算法更加高效&#xff0c;减少了更新时的开销&#x…

【电路笔记】-快速了解数字逻辑门

快速了解数字逻辑门 文章目录 快速了解数字逻辑门1、概述2、集成电路的分类3、摩尔定律4、数字逻辑状态5、数字逻辑噪声6、简单的基本数字逻辑门7、基本 TTL 逻辑门8、发射极耦合数字逻辑门9、集成电路的“74”子族10、基本 CMOS 数字逻辑门数字逻辑门是一种电子电路,它根据其…

CI/CD实战-jenkins结合ansible 7

配置主机环境 在jenkins上断开并删除docker1节点 重新给master添加构建任务 将server3&#xff0c;server4作为测试主机&#xff0c;停掉其上后面的docker 在server2&#xff08;jenkins&#xff09;主机上安装ansible 设置jenkins用户到目标主机的免密 给测试主机创建用户并…

STL —— string(终)

目录 1. swap() 函数的模拟实现 2. find() 函数的模拟实现 3. substr() 函数的模拟实现 4. operator()的重载模拟实现 5. << 和 >> 重载的模拟实现 6. getline() 的重载 7. 拷贝构造的现代写法 8. 赋值重载的现代写法 本片文章还是主要讲解 string 类中剩…

运维经验|Linux虚拟机如何挂载磁盘

&#x1f4eb; 作者简介&#xff1a;「六月暴雪飞梨花」&#xff0c;专注于研究Java&#xff0c;就职于科技型公司后端工程师 &#x1f3c6; 近期荣誉&#xff1a;华为云云享专家、阿里云专家博主、腾讯云优秀创作者 &#x1f525; 三连支持&#xff1a;欢迎 ❤️关注、&#x…

路径规划——搜索算法详解(五):Dynamic A Star(D*)算法详解与Matlab代码

昨天休息了一天&#xff0c;今天继续学习搜索算法&#xff01;前几天已经分别介绍了Dijkstra算法、Floyd算法、RRT算法、A*算法&#xff0c;无独有偶&#xff0c;上述算法都只适用于静态环境下两点规划的场景&#xff0c;但是大部分场景是实时变化的&#xff0c;这对规划算法提…

二叉树的深度优先遍历(前中后)

1. 前序遍历 前序遍历是先输出根节点&#xff0c;再输出左子树&#xff0c;最后输出右子树。 2. 中序遍历 中序遍历&#xff0c;左子树&#xff0c;根节点&#xff0c;右子树 3. 后序遍历 左子树&#xff0c;右子树&#xff0c;根节点 4. 代码实现&#xff08;递归形式&…

Vulnhub:BROKEN: GALLERY

目录 信息收集 1、arp 2、nmap 3、nikto 4、whatweb WEB wen信息收集 目录扫描 进制转换 ssh登录 提权 信息收集 1、arp ┌──(root㉿ru)-[~/kali/vulnhub] └─# arp-scan -l Interface: eth0, type: EN10MB, M…

向量点乘有哪些作用呢

如下&#xff1a; 1.找到两个向量之间的夹角(不用多说) 2.求一个向量投影在另一个向量的投影&#xff1a; 我们把图中b的在a上的投影向量称作b1吧&#xff0c;因为b1就在a上&#xff0c;所以只需要求出b1的大小&#xff0c;然后乘以a的单位向量&#xff0c;我们就得到向量b1了…

Bezier曲线

1. 实验要求 2. Bezier曲线的原理 以及 公式推导 参考贝塞尔曲线&#xff08;Bezier Curve&#xff09;原理及公式推导_bezier曲线-CSDN博客 Bezier曲线的一些特性&#xff1a; 使用n个控制点来控制曲线形状 曲线通过起始点和终止点&#xff0c;接近但不通过中间点 2.1 直观…

结构化绑定optional(C++基础)

结构化绑定 处理多个返回值的操作&#xff1a;C17提出 之前多返回值喜欢用struct来返回。现在会做成元组&#xff0c;下图中设置C17的版本&#xff0c;不要设置错为C语言标准。 #include<iostream> #include<string> #include<tuple> std::pair<std::st…

ubuntu下给不同串口设置别名

目录 一、绑定设备ID 1.查看设备ID 2.编写usev规则 3.重新加载usev规则 4.查看 二、绑定USB端口号 1.先插入一个串口&#xff0c;查看USB设备信息 2.查看USB转串口信息 3.编写usev规则 4.重新加载usev规则 5.查看 在Ubuntu环境下&#xff0c;有时候工控机或者arm开…

新增收货地址

目录 &#x1f9c2;1.创建controller层 &#x1f953;2.创建service层 &#x1f32d;3.注意细节 &#x1f37f;4.避免dao数据暴漏 1.创建controller层 controller不做逻辑操作&#xff0c;只接受前端的数据 1.添加Api设置swagger模块名称2.RestController以json形式返回…

CAJViewer8.1下载地址及安装教程

CAJViewer是中国学术期刊&#xff08;CAJ&#xff09;全文数据库的专用阅读软件。CAJViewer是中国知识资源总库&#xff08;CNKI&#xff09;开发的一款软件&#xff0c;旨在方便用户在线阅读和下载CAJ数据库中的学术论文、期刊和会议论文等文献资源。 CAJViewer具有直观的界面…

2000-2021年各省技术市场发展水平数据(原始数据+计算结果)

2000-2021年各省技术市场发展水平数据&#xff08;原始数据计算结果&#xff09; 1、时间&#xff1a;2000-2021年 2、来源&#xff1a;国家统计局、统计年鉴 3、范围&#xff1a;30省 4、指标&#xff1a;技术市场成交额、国内生产总值、技术市场发展水平 5、计算说明“技…

java字符串(一)-- 字符串API,StringBuffer 和 StringBuilder,Object

String字符串相关的类 String的特性 String类&#xff1a;代表字符串。Java 程序中的所有字符串字面值&#xff08;如"abc" &#xff09;都作为此类的实例实现。String类是引用数据类型。 在 Java 8 中&#xff0c;String 内部使用 char 数组存储数据。 public fi…