C++ 实现Manacher算法

news2024/10/7 8:28:11

前言

  • Manacher算法是一种回文串查找算法,专门用于处理查找字符串中的回文子串操作。
  • 虽然这个算法本身只是用于查找回文子串,但是它的查找思想还是非常值得学习的。
  • 由于Manacher算法是基于暴力解法优化而来的,所以在阅读正式的算法之前,需要先了解暴力解法的思路和过程。
    leetcode指路:最长回文子串

一、暴力解法

1.重构字符串

  • 这里我们采取从中间向两边扩散的方式,动态查找可能存在的最大回文串。
    例如以0下标位置的为中心的最长回文子串为a
    在这里插入图片描述
    1下标位置的为中心的最长回文子串为aba
    在这里插入图片描述
    2下标位置的为中心的最长回文子串为a
    在这里插入图片描述

  • 但是这种求解过程有一定局限性,它无法判断长度为偶数的回文串

  • 所以如果我们需要同时判断偶数个的子串,就需要从两个值之间的位置出发判断

  • 这里的选择,是通过直接添加补间字符的方式来解决这个问题。
    例如:
    在这里插入图片描述
    这样的话我们就可以判断偶数串的回文情况。
    至于为何前后各额外添加了一个#,则是为了后面方便计算使用。

  • 重构字符串代码:

	static void manacherString(const string& str, string& s)
	{
		s.clear();
		s.reserve(str.size() * 2 + 1);
		s.push_back('#');
		for (char c : str)
		{
			s.push_back(c);
			s.push_back('#');
		}
	}

2.暴力解法

  • 依然是遍历字符串,从每个位置开始向两边扩展来找到最大的回文子串,即while循环中的逻辑
    其中while循环的判断条件是查看是否越界,if语句用于判断边界上的两个字符是否相等。
  • 这里首先定义一个当前半径r,然后进入while循环,只要while循环不跳出,半径就自增,通过这种方式来查找最大半径。(0位置不用看,自己和自己必定相等)
  • 又因为我们这里查找的是已经重构了的字符串,所以此时 真实的子串长度 == 重构子串长度 - 1,这也是返回时,返回maxValue - 1 的原因
	static int maxLcpsLength(const string& str)
	{
		if (str.empty()) return 0;
		string mStr;
		manacherString(str, mStr);
		int maxValue = 0;
		for (int i = 0; i < mStr.size(); i++)
		{
			int r = 1;
			// 找到以i 为中点的最大回文子串
			while (i + r < mStr.size() && i - r > -1)
			{
				// 不相等就跳出
				if (mStr[i + r] != mStr[i - r]) break;
				//相等就扩大
				r++;
			}
			// 更新最大值
			maxValue = max(r, maxValue);
		}
		return maxValue - 1;
	}

这个方法还是相对而言比较简单的,而Manacher就是在这个解法上进行优化,过滤了很多重复计算过的优化算法

二、Manacher算法

有了以上的知识作为基础,我们在来说Manacher算法的原理,它其实是一种动态规划的思想

  • 将之前所求得的所有最大回文子串半径存在一个辅助数组中,并应用到之后的求解过程中
    要如何应用我们已知的回文串信息呢?
  • 这里需要记录两个关键信息:已知最远右边界R,已知最远右边界中心点C
    例如下图中:如果当指针遍历过C点时,发现它的回文半径可以衍生到R位置,是目前遍历过的里面最远的,那么就更新CR的信息,直到发现更远的边界下标为止。
    在这里插入图片描述
  • 假设我们有i'关于中心点C与当前位置i对称
    在这里插入图片描述
  • 首先我们已经求解过i'位置的回文信息
  • 先说结论:i'位置上的最大回文子串半径可以帮助我们过滤一定的特殊情况运算
  • iR范围以内时,我们可以利用已知的回文串对称的性质,将iR范围以内的运算全部过滤掉.
    之后的情况可以分为几种情况讨论
    在这里插入图片描述
  1. i位置越过了R边界范围,此时i'也在R'范围之外
  2. i位置没有越过R边界范围时,若i'位置的最大回文子串在已知最远回文串以内i'位置最大回文串左边界大于R'
  3. i位置没有越过R边界范围时,若i'位置的最大回文子串在已知最远回文串边界上i'位置最大回文串左边界等于R'
  4. i位置没有越过R边界范围时,若i'位置的最大回文子串越过了已知最远回文串,i'位置最大回文串左边界小于R'

  1. 第一种情况是不可避免运算的,此时已有的信息不能帮助我们进行下一步的运算,此时需要暴力求解i位置的最大回文串
  2. 第二种情况下,因为i'R' ~ R以内,且边界不在R'上,所以i'最大回文串两边的元素一定不相等,且这两个元素都在R' ~ R以内,由此可得,i的最大回文串一定与i'的最大回文串相等。
  3. 第三种情况下,由于边界有交集,所以无法证明R范围以外不存在以i为中心,更大的回文串。这种情况下,可以过滤半径在R' ~ R范围以内的运算
  4. 第四种情况下,已知i'最大回文串越界,又由于R' ~ R范围两边的元素一定不相等,所以i'的最大回文串在R'范围外的元素与iR界外的元素一定不相等,所以这种情况下R' ~ R范围外也不存在以i位置为中心更大的回文串。

由于上述分析可得:我们最多可以过滤的运算数量有3种情况

  1. 过滤不了,暴力扩
  2. 过滤i'
  3. 过滤R - i个,即i' - R'

基于上述理论,我们可以对之前的算法进行优化,下面给出代码

三、完整代码

class Solution
{
	static void manacherString(const string& str, string& s)
	{
		s.clear();
		s.reserve(str.size() * 2 + 1);
		s.push_back('#');
		for (char c : str)
		{
			s.push_back(c);
			s.push_back('#');
		}
	}
public:
	static int maxLcpsLength(const string& str)
	{
		if (str.empty()) return 0;
		string mStr;
		// 重构字符串
		manacherString(str, mStr);
		int* mArr = new int[mStr.size()];
		for_each(mArr, mArr + mStr.size(), [](int ele) { ele = 0; });	
		int C = -1;
		int R = -1;
		int max = 0;
		for (int i = 0; i < mStr.size(); i++)
		{
			// 找到可以跳过的扩充区间
			mArr[i] = R > i ? min(mArr[2 * C - i], R - i) : 1;
			// 找到以i 为中点的最大回文子串
			while (i + mArr[i] < mStr.size() && i - mArr[i] > -1 
				&& mStr[i + mArr[i]] == mStr[i - mArr[i]])
			{
				//相等就扩大
				mArr[i]++;
			}
			// 更新最大边界
			if (i + mArr[i] > R)
			{
				R = i + mArr[i];
				C = i;
			}
			// 更新最大值(也可以值即记录值)
			max = mArr[max] > mArr[i] ? max : i;
		}
		return mArr[max] - 1;
	}

};

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

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

相关文章

arthes—线上debug好帮手

arthes简介 以下是arthes官网原文&#xff1a; 通常&#xff0c;本地开发环境无法访问生产环境。如果在生产环境中遇到问题&#xff0c;则无法使用 IDE 远程调试。更糟糕的是&#xff0c;在生产环境中调试是不可接受的&#xff0c;因为它会暂停所有线程&#xff0c;导致服务暂…

粒子滤波原理和MATLAB代码实现

理论基础1&#xff1a; (a) Prediction Use the transition equation to propagate the particles: (b) Update Use the measurement equation to obtain measurements of the propagated particles and their standard deviations: (in the case of our program, ym is obt…

如何在 Manjaro Linux 上安装 ONLYOFFICE 桌面编辑器

ONLYOFFICE 桌面编辑器是一款免费开源办公套件&#xff0c;其中包括适用于文本文档、电子表格与演示文稿的离线编辑器。同时&#xff0c;您还可将应用程序连接至云端&#xff08;ONLYOFFICE、Nextcloud 等&#xff09;以便在线开展文档协作。该应用的源代码已根据 AGPL v.3.0 许…

业务中台10讲2.0合辑(推荐收藏)

目录V3.0迭代内容&#xff1a; 增加最近更新的中台系列文章至本目录&#xff1b;根据最新热点修订并调整部分未更新内容方向&#xff1b;为各文章标注《中台产品经理宝典》书中原文出处&#xff1b;本目录使用方法&#xff1a; 本目录推文为中台内容系列中的业务中台子类新原…

华润微功放CS3850EO,2×40W D 类音频功率放大电路,替换:智浦芯CS8673,TI的TAS5780、TAS5754,国产功放

1、概述 CS3850EO 是一款典型输出功率为 40W 立体声的 D 类音频功率放大电路&#xff0c;适用于拉杆音箱、高级桌面音响等场合。 特点 ● 工作电压范围&#xff1a;8V~26V ● 典型输出功率&#xff1a;30W2 20V、8Ω、THD10% 40W2 18V、4Ω、THD10% 50W2 26.5V、8Ω、…

你以为Shell只是命令行?读懂这篇文,给你的工作赋能

可以使用adb tcpip 端口在Android设备上启动一个指定的端口&#xff0c;然后使用adb connect Android设备ip:端口远程连接Android设备。 uiautomator 是一个 java 库&#xff0c;包含用于创建自定义功能UI测试的API&#xff0c;以及用于自动执行和运行测试的执行引擎。使用uiau…

Transformer与看图说话

&#x1f3c5;&#x1f3c5;&#x1f3c5;&#x1f3c5;&#x1f3c5;&#x1f3c5;&#x1f3c5;&#x1f3c5;&#x1f3c5;&#x1f3c5;&#x1f3c5;&#x1f3c5;&#x1f3c5;&#x1f3c5;&#x1f3c5;&#x1f3c5; 一年一度的【博客之星】评选活动已开始啦 作为第一…

Redis的持久化技术

1. 前言 今天呢&#xff0c;我们来了解下Redis的持久化技术。都知道Redis是内存型key-value型数据库。其实叫缓存中间件更合适。既然是内存性数据库就知道存入磁盘的必要性了。所以就需要持久化技术来支持了 2. 合适人群 对Redis 持久化技术不了解的人 3. RDB RDB 其实就是Re…

推荐三款 Mac 上的理财神器 iCompta、Rublik、UctoX

推荐三款 Mac 上的理财神器 iCompta、Rublik、UctoX 今天推荐三款理财神器&#xff0c;像个人的话可以使用 iCompta&#xff08;个人财务管理&#xff09;一款软件就好了&#xff0c;但有些朋友可能有关注汇率的需求&#xff0c;可以使用 Rublik&#xff08;汇率动态&#xff…

尚硅谷密码学

密码学1. 密码学1.1. 密码学基本概念1.2 密码学的历史1.2.1 古典密码学1.2.1.1. 替换法1.2.1.2移位法1.2.1.2 古代密码学的破解方式1.2.2 进代密码学1.2.3 现代密码学1.2.3.1 散列函数1.2.3.2 对称密码1.2.3.3 非对称密码1.2.4 如何设置密码才安全1.2.5 ASCII编码1.3 凯撒加密1…

Ant Design Pro入门

目录 一&#xff1a;了解Ant Design Pro 二&#xff1a;快速入门 一&#xff1a;了解Ant Design Pro Ant Design Pro 是基于Ant Design的一个开箱即用的&#xff0c;企业级中后台前端/设计解决方案。 效果&#xff1a;源码地址&#xff1a;https://github.com/ant-design/ant…

Linux制作和使用动静态库

文章目录一、概念1.1 动态库和静态库1.2 动态链接和静态链接二、制作第三方库2.1 生成静态库① 制作静态库② 使用静态库2.2 生成动态库① 制作动态库② 使用动态库三、相关题目一、概念 1.1 动态库和静态库 静态库与动态库本质都是一堆目标文件(xxx.o)的集合&#xff0c;库的…

MySQL 索引之道

文章目录1. 索引的介绍2. 索引的本质3. 索引的结构3.1 Hash3.2 B树3.3 常见面试题之为什么用B树4. 索引的分类4.1 功能逻辑层次4.2 存储形式层次5. 索引的失效5.1 最左前缀原则5.2 索引失效的场景6. 索引常见面试题7. 总结及参考文献1. 索引的介绍 索引是通过某种算法&#xf…

快速学习一门新技术的工作原理(十步学习法来自软技能)

快速学习一门新技术的工作原理 ●如何开始——要想开始使用自己所学的&#xff0c;我需要掌握哪些基本知识&#xff1f; ●学科范围——我现在学的东西有多宏大&#xff1f;我应该怎么做&#xff1f;在开始阶段&#xff0c;我不需要了解每个细节&#xff0c;但是如果我能对该学…

后台交互-首页

目录 后台准备 pom.xml 配置数据源 mybatis-generator 整合mybatis 准备前端的首页的数据 Promise 封装request 会议展示 后台准备 springbootmybatis pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://ma…

数据结构之链表(java语言实现)

链表的底层储存结构&#xff1a; 相对于数组这一需要连续、足够大空间的数据结构&#xff0c;链表只需要利用“指针”将一组零碎的空间(在链表中称之为节点)串联起来&#xff0c;这样就可以避免在创建数组时一次性申请过大的空间二导致有可能创建失败的问题!!! 同时比较两种数…

20221228英语学习

今日短文 Words and Phrases to Avoid in a Difficult Conversation Difficult conversations are difficult for a reason, and when you’re anxious or stressed out, it’s easy to say the wrong thing.And it doesn’t matter how prepared you are.Your best laid plan…

UDP协议与TCP协议详解

UDP协议详解 UDP&#xff0c;即User Datagram Protocol&#xff0c;用户数据报协议 UDP协议的特点&#xff1a;无连接&#xff0c;不可靠传输&#xff0c;面向数据报&#xff0c;全双工 无连接&#xff1a;知道对端的IP和端口号就直接进行传输&#xff0c;不需要建立连接&am…

深入讲解Linux中断子系统--Workqueue

说明&#xff1a; Kernel版本&#xff1a;4.14ARM64处理器&#xff0c;Contex-A53&#xff0c;双核使用工具&#xff1a;Source Insight 3.5&#xff0c; Visio 1. 概述 Workqueue工作队列是利用内核线程来异步执行工作任务的通用机制&#xff1b;Workqueue工作队列可以用作中…

以前的互联网时代,其实就是一个以互联网技术为主导的年代

事实上&#xff0c;以往&#xff0c;我们所经历的那个互联网玩家频出的年代&#xff0c;其实就是一个以互联网技术为主导的年代。在那样一个年代里&#xff0c;互联网技术几乎是解决一切痛点和难题的万能解药&#xff0c;几乎是破解一切行业痛点和难题的杀手锏。任何一个行业&a…