【数据结构与算法】Manacher算法
🌠作者:@阿亮joy.
🎆专栏:《数据结构与算法要啸着学》
🎇座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根
目录
- 👉前言👈
- 👉Manacher 算法👈
- 👉最长回文子串👈
- 👉总结👈
👉前言👈
如果给定一个字符串 str,如何求解该字符串的最长回文子串(注:子串必须是连续的,子序列不要求连续)。如字符 str 为 abc12320d1,其最长回文子串是 232,而不是 回文子序列 12321。如果一个字符串是回文串,那么该字符串一定有个对称轴,使得左右两边的字符是对称的。比如:字符串 abcba 的对称轴是字符 c(实轴),字符串 abba 的对称轴是一条虚轴(不是关于某个字符对称)。
那么我们要求字符串的最长回文子串,我们很容易想到一种暴力的方法:遍历字符串的每一个字符,从该字符为中心向左右两边扩(左边和右边的字符相等就扩,不相等就停止),这样就能得到最长回文子串了。这种方法的时间复杂度是 O(N^2),非常的暴力,而且这种方法不能够解决字符串长度为偶数的情况。如:字符串 122131221,因为回文串长度为偶数时,其对称轴是虚轴,这样就无法保证每个字符向左右两边扩都能得到以该字符为中心的最长回文串。
那如何保证能够得到偶数的回文串呢?这就需要学习本篇博客要介绍的 Manacher 算法。
👉Manacher 算法👈
Manachar 算法主要是处理字符串中关于回文串的问题的,它可以在 O(N) 的时间处理出以字符串中每一个字符为中心的回文串半径,由于将原字符串处理成两倍长度的新串,在每两个字符之间加入一个特定的特殊字符,因此原本长度为偶数的回文串就成了以中间特殊字符为中心的奇数长度的回文串了。
Manacher 算法提供了一种巧妙的办法,将长度为奇数的回文串和长度为偶数的回文串一起考虑。具体做法是:在原字符串的每个相邻两个字符中间插入一个分隔符,同时在首尾也要添加一个分隔符,分隔符可以是原串中的子串,但一般情况下都是使用 # 号作为分隔符。
如果 Manacher 算法也像上图的做法来求最长回文子串的话,那么它的时间复杂度也是 O(N^2),那 Manacher 算法是如何将时间复杂度优化到 O(N) 的呢?我们一起来看一下!
Manacher 算法的优化就是通过已知信息来做到时间复杂度的优化。首先,我们需要知道回文半径和回文直径的概念。见下图所示:
知道回文半径和回文直径后,我们还需要知道一个概念—— 回文半径数组,就是我们将已经求得的回文半径放在数组中,那么该数组就被称为回文半径数组。还有最后两个概念,一个是回文右边界,另一个是回文中心。回文中心比较好理解,就是回文串的中心点下标。回文右边界是以每个字符为中心扩出来的回文串的右边界,它是一个整型。在扩的过程中,如果新生成的回文串的右边界比原来右边界还要右,这时候就需要更新回文右边界;否则不需要更新。如果回文右边界更新,回文中心就需要更新;而如果回文右边界没有更新,回文中心也不需要更新。
知道了上面的全部概念后,我们就来学习 Manacher 算法。Manacher 算法在更新回文数组的时候,会遇到两种情况:字符的下标超出回文右边界和字符的下标在回文右边界的范围内。
当字符的下标超出回文右边界时,我们就以该字符为中心向左右暴力两边扩,然后更新回文右边界。
当字符的下标在回文右边界的范围内,这时候就可以通过已用的信息(回文数组)来进行优化了。
字符下标在回文右边界内这种情况又可以根据 i’ 回文区域的不同分为三个小类:
- 第一小类:i’ 的整个回文区域都在 left 到 right 的范围内
- 第二小类:i’ 回文区域的左边界小于 left
- 第三小类:i’ 回文区域的左边界等于 left
Manacher 算法伪代码
// 返回值是回文半径数组
vector<int> Manacher(string& s)
{// 1221 -> #1#2#1#2#1#s 经过处理变成了 strvector<int> pArr(str.size(), 0); // 回文半径数组int right = -1; //回文右边界int center = -1; // 回文中心for(int i = 0; i < str.size(); ++i){if(i在right的外部){以i为中心,向左右两边暴力扩,回文右边界right变大}else{if(i'的回文区域在left到right范围内){pArr[i] = pArr[2 * center - i] // 堆成性质}else if(i'回文区域的左边界小于left){pArr[i] = right + 1 - i; // 加一的原因是回文半径需要加上中心点i}else // i'回文区域的左边界等于left{从right范围之外的字符开始往外扩,然后确定回文半径pArr[i]第一次扩失败了,回文右边界right不变否则,回文右边界right变大}}}return pArr;
}
很显然,Manacher 算法的时间复杂度是 O(N)。
👉最长回文子串👈
根据 Manacher 算法的伪代码,我们可以改成以下的精简版本的代码
class Solution
{
public:string longestPalindrome(string s) {// babad --> #b#a#b#a#d// 加入分隔符#string tmp = "#";for(auto ch : s){tmp += ch;tmp += "#";}vector<int> pArr(tmp.size(), 0); // 回文半径数组int center = -1; // 回文中心int right = -1; // 回文右边界的再往右一个位置,最右的回文区域是R-1位置int start = 0; // 最长回文子串的左边界int end = -1; // 最长回文子串的右边界int Max = INT_MIN; // 最长回文子串的半径,即最长回文串的直径 / 2 + 1// 那么 Max - 1 就是原字符串的最长回文子串的长度// 每个位置都要求回文半径int size = tmp.size();for(int i = 0; i < size; ++i){// i至少的回文区域,先更新给pArr[i]// 不需要扩就能知道i的最小回文区域// i在right外时,需要暴力扩,i的回文区域至少包括自己// i在right内,第一小类和第二小类是能够直接拿到的// 对于第一和第二小类,i的回文半径是(pArr[2 * center - 1]、right - i)中的较小值pArr[i] = right > i ? min(pArr[2 * center - i], right - i) : 1;// 第三小类是需要向左右两边扩才能确定的往,循环条件保证往外扩时不越界// 第一和第二小类第一次扩就会扩失败while(i + pArr[i] < size && i - pArr[i] > -1){if(tmp[i + pArr[i]] == tmp[i - pArr[i]])++pArr[i];elsebreak;}// 更新回文右边界和回文中心if(i + pArr[i] > right){right = i + pArr[i];center = i;}// 注:i+pArr[i]大于right并不意味着以i为中心的回文子串就是最长的回文子串// 而如果以i为中心的回文子串就是最长的子串,那么i+pArr[i]一定大于right// 更新最长回文子串的半径、左边界和右边界if(pArr[i] > Max){Max = pArr[i];// 因为right是回文右边界的下一个位置,且最长回文子串// 的左边界加右边界等于两倍的center,所以可以推出// start + right - 1 = 2 * centerstart = 2 * center + 1 - right;end = right - 1;}}// 将最长回文子串还原出来// Max - 1 和 (end + 1 - start) / 2都是最长回文子串的长度string ret;for(int i = start; i <= end; ++i){if(tmp[i] != '#')ret += tmp[i];}return ret;}
};
👉总结👈
本篇博客主要讲解什么是 Manacher 算法以及 Manacher 算法是如何求解最长回文子串的。那么以上就是本篇博客的全部内容了,如果大家觉得有收获的话,可以点个三连支持一下!谢谢大家!💖💝❣️
相关文章:

【数据结构与算法】Manacher算法
🌠作者:阿亮joy. 🎆专栏:《数据结构与算法要啸着学》 🎇座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根 目录👉…...

【CMake】CMake构建C++代码(一)
在Linux开发过程中,难免会用到CMake来构建你的代码。本文将说明如何构建自己的代码,将自己的代码变为共享库,共其他代码使用。 文章目录在Linux开发过程中,难免会用到CMake来构建你的代码。本文将说明如何构建自己的代码ÿ…...

让我们,从头到尾,通透I/O模型
什么是IO 一句话总结 IO就是内存和硬盘的输入输出 I/O 其实就是 input 和 output 的缩写,即输入/输出。 那输入输出啥呢? 比如我们用键盘来敲代码其实就是输入,那显示器显示图案就是输出,这其实就是 I/O。 而我们时常关心的磁盘…...

Word控件Spire.Doc 【Table】教程(16):C#/VB.NET:在 Word 表格中插入或提取图像
Spire.Doc for .NET是一款专门对 Word 文档进行操作的 .NET 类库。在于帮助开发人员无需安装 Microsoft Word情况下,轻松快捷高效地创建、编辑、转换和打印 Microsoft Word 文档。拥有近10年专业开发经验Spire系列办公文档开发工具,专注于创建、编辑、转…...

C++如何实现系统语言切换功能,MessageBox的确认/取消按钮语言显示如何跟程序一致
文章目录前言 一、新建工程二、添加多国语言的资源三、程序语言设置四、语言切换五、字符串处理六、MessageBox的问题七、相关函数和类型参考文章前言 目前很多软件都是要出口到多个国家,多个地区,因此,为软件提供多国语言支持就成为了一个基…...

计算机组成原理学习笔记:循环冗余校验码
循环冗余校验码 CRC 码 循环冗余校验码 (cyclic redundancy Check, CRC) 十进制除法 从熟悉的十进制出发,假设现在你要给另一个人传送882这样的一个10进制数据,为了防止传送数据的过程中某一个数据发生错误你可以和你的另一个小伙伴约定一个除数&…...
Educational Codeforces Round 143 (Rated for Div. 2) A — C
Educational Codeforces Round 143 (Rated for Div. 2) 文章目录A. Two Towers题目大意题目分析codeB. Ideal Point题目大意题目分析codeC. Tea Tasting题目大意题目分析codeA. Two Towers 题目大意 有两个有红蓝两种颜色组成的塔,每次操作可以将其中一个塔顶的色…...

【Unity VR开发】结合VRTK4.0:将浮点数从交互器传递到可交互对象
语录: 愿你熬得过万丈孤独,藏得下星辰大海。 前言: 默认情况下,交互器只能将单个布尔操作传递给可交互对象,后者控制可交互对象上的抓取操作。在其他时候,交互器中的其他操作可能希望传递给可交互对象&…...
【图像分类】基于PyTorch搭建卷积神经网络实现MNIST手写数字识别(附项目完整代码)
写在前面: 首先感谢兄弟们的关注和订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。 在【图像分类】基于PyTorch搭建LSTM实现MNIST手写数字体识别(单向LSTM,附完整代码和数据集)文章中,我们使用了…...

4.4 MQC
1. 实验目的 熟悉MQC的应用场景掌握MQC的配置方法2. 实验拓扑 实验拓扑如图4-10所示: 图4-10:MQC 3. 实验步骤 (1) IP地址的配置 AR1的配置 <Huawei>system-view...

ClickHouse列存储(十一)—— ClickHouse
文章目录一、重点内容:1.数据库基本概念2.列式存储3.clickHouse存储设计4.clickHouse典型应用场景二、准备工作:1、了解数据库基本概念2、了解列式存储相关概念3、了解ClickHouse存储设计4、了解 ClickHouse典型应用场景三、详细知识点介绍:1…...

公司来了个卷王,真让人奔溃
2022年已经结束结束了,最近内卷严重,各种跳槽裁员,相信很多小伙伴也在准备今年的金三银四的面试计划。 在此展示一套学习笔记 / 面试手册,年后跳槽的朋友可以好好刷一刷,还是挺有必要的,它几乎涵盖了所有的…...

什么是refresh?Spring refresh 流程
refresh 是 AbstractApplicationContext 中的一个方法,负责初始化 ApplicationContext 容器,容器必须调用 refresh 才能正常工作。它的内部主要会调用 12 个方法,我们把它们称为 refresh 的 12 个步骤:1. prepareRefresh2. obtain…...

Python登陆系统
前言 #源码见文末公众号哈# 登录系统 一个简单的登录系统包含了登录账户、注册账户、修改密码以及注销账户的操作。 1. 登录账户 登录系统主要需要判断账户是否存在,不存在就注册一个账户,如果第一次登录系统,我们需要先新建一个文件&…...
【新2023】华为OD机试 - 重组字符串(Python)
华为 OD 清单查看地址:blog.csdn.net/hihell/category_12199275.html 重组字符串 题目 给定一个非空字符串 S,其被 N 个‘-’分隔成 N+1 的子串,给定正整数 K, 要求除第一个子串外,其余的子串每 K 个字符组成新的子串,并用‘-’分隔。 对于新组成的每一个子串,如果它…...
视频监控流程图
<html> <head> <meta http-equiv"Content-Type" content"text/html; charsetUTF-8"/> <link rel"stylesheet" type"text/css" href"visio.css"/> <title> 视频监控流程图 </title> <…...

普通单双面板的生产工艺流程之图形转移,华秋一文告诉你
衔接上文,继续为朋友们分享普通单双面板的生产工艺流程。 如图,第五道主流程为图形转移。 图形转移的目的为: 利用光化学原理,将图形线路的形状转移到印制板上,再利用化学原理,将图形线路在印制板上制作出…...
1.8 providers
生成providersnest g service <name>providers的注入方式构造函数注入Injectable() export class KeywordService {constructor(private readonly httpService: HttpService,private readonly pro: ProService,) {} }Inject()注入export class KeywordController {Inject…...

如何编写一个基本的 Verilog Module(模块)
1、概述这篇文章主要介绍了 Verilog 在 FPGA 设计中的概念和使用方法。首先讨论使用模块(module)关键字构造 Verilog 设计的方式,以及这与所描述的硬件的关系。这包括对参数、端口(port)和例化(instantiato…...

让乔布斯想要「发动核战争」的 Android,为何成了占有率最高的系统?
2008 年 9 月 23 日,Apple 的创始人和 CEO 史蒂夫乔布斯像往常一样走进了公司,此时距离初代 iPhone 的发布会才过了一年半,这款充满了争议的产品就像一块从山崖滚落的巨岩,一路电光石火的给手机市场的《小石潭记》来了场焚书坑儒。…...

测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...

手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...

el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...