Manacher算法解析

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: Manacher算法解析

前言

Manacher算法目前了解到的适用范围只有求回文字符串。以下的所有解析也都会围绕求最大回文字符串来展开(求最大回文子串)。


什么是回文字符串?

回文字符串就是如“abba”,"abcba",这种围绕中心完全对称的字符串。

题目:在字符串里求出他的最大回文子串

如:12323,他的最大回文子串就是232和323。

接下来我们引入几个概念  对称中心,顾名思义就是对称最中心的那个数,比如121的对称中心就是2.因为我们这里需要把对称中心由一个数字具体表示,所以我们没法表示偶数数量的对称中心(比如1221的对称中心是22中间的对称轴)。那这里怎么解决呢?在每个数字的两边插入任意相同的字符(为什么是任意呢?因为原先对称的串在插入对称的字符后依然对称,所以随便插入什么字符都不会影响原先的结果),这里以插入#举例,插入后的效果是这样的。

插入完之后原来偶数数量对称的串都会变成奇数,并且也不会产生新的对称字符串(具体不理解的手画一遍就懂了),并且我们根据变化之后的(字符串长度/2)就可以得到原来字符串的长度了。

这时候可以用一种暴力解法来完成功能,遍历一遍数组,遍历过的每个元素都执行一遍操作:检验遍历过的元素的左右两边是否相等,如果相等则继续往外检验是否相等直到遇到左右不相等的字符为止或者碰到数组的边界为止。(举个例子:看上面那幅图,当我遍历到b时,我会检测b左边一位和b右边一位是否相等,这里是相等所以我继续将往左两位和往右两位进行比较)。这样就可以得到我们以该字符为中心的回文字符串的长度。然后再拿变量去记录最长的长度和下标,我们就能得到最终最长子字串的长度了。虽然这样已经能够实现功能,但他还有能够优化的地方,Manacher算法就是在这的基础上优化的

为了后面的运算能够快捷,我们需要申请一个长度等于加#后长度的数组,用来存储已经遍历过的每个位置的(中心的最长子字符串长度/2)。这里为什么要除以2呢,不是因为之前加过#要算回原来的大小,而是我们后面需要知道串伸向最右边的那个点(先不用知道为什么),而我们现在已经有了中心点的下标了,这时候直接加上一半的长度就可以得到该子串最右边的坐标了。

现在开始我们进行Manacher算法的讲解。

第一种情况   maxRight(遍历过的回文子串达到过最右的位置,一开始是0)小于现在所遍历到的位置,那就按照之前的解法一个个的遍历并记录每个的最大长度/2并更新maxRight.

第二种情况 maxRight大于现在所遍历到的位置。那么这时候将再分成三种情况。这里我们设当前遍历到的元素为 i,子字符串达到最右边的距离为maxRight,达到maxRight的子串中心center,i关于center的对称点 j,还有记录之前所遍历所有元素的最长臂长long[]从此以后臂长就是最大长度/2)

maxRight - i > long[j]

如图,假设这的的long[j] = 1,那么我们可以直接得到long[i] = 1且不需要做额外的对比。这是为什么呢?因为在maxRight内,i两边的内容和j两边的内容是完全相等的(因为关于center对称),而 j处的最大子串由不超过center的子串(由式子得出来的),所以long[i] = long[j]

② maxRight - i = long[j]

这里直接说结论,当相等时,检测i的最长子串可以直接从maxRight右边开始,maxRight左边至i的位置一定是回文的(因为j与i对称且j在maxRight'之前都是对称),然后maxRight的右边由于是为检验过的区域所以不知道能不能与i组成回文,只要直接从maxRight的右边检测就行

③ maxRight - i < long[j]

当满足上述式子时,是不是可以直接从maxRight右边开始检测呢?不必,因为这时候的结论是long[i] = maxRight - i ,这是因为如果这时候i的回文串能再往外多一个,就说明那个点与j在maxright'左边的那个点相等了,那就满足了center的中心回文的条件,这是矛盾的。所以这个时候long[i] = maxRight - i

上述的几种情况是可以进行合并的,可观察①和③。当maxRight - i > long[j] 时 long[i] = long[j],当maxRight - i < long[j] 时 long[i] = maxRight - i,再看②,是直接long[i] = long[j]然后再往后搜索。那我们就可以合并这三个式子为long[i] = min(maxRight - i,long[j])并再往后搜索

接下来就是重复上面的操作,记得每算完一个元素在对应的long里写进去他的臂长,也要及时修改maxRight的值。


总结

搜索最大回文子串,当i大于maxRight时,按照暴力解法从中心往外拓展搜索并记录搜索过位置的long[i]和maxRight;i小于maxRight时,long[i] = min(maxRight - i,long[j])并再往后搜索。

复杂度对比,直接暴力解发的时间复杂度是O(n^3),以搜索子串中心为目的的暴力解发时间复杂度是O(n^2),动态规划时间复杂度为O(n^2),Manacher算法的时间复杂度是O(n),空间复杂度是O(n)。


相关文章
|
1月前
|
算法 前端开发 数据处理
小白学python-深入解析一位字符判定算法
小白学python-深入解析一位字符判定算法
47 0
|
30天前
|
存储 算法 Java
解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用
在Java中,Set接口以其独特的“无重复”特性脱颖而出。本文通过解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用。
41 3
|
1月前
|
搜索推荐 算法
插入排序算法的平均时间复杂度解析
【10月更文挑战第12天】 插入排序是一种简单直观的排序算法,通过不断将未排序元素插入到已排序部分的合适位置来完成排序。其平均时间复杂度为$O(n^2)$,适用于小规模或部分有序的数据。尽管效率不高,但在特定场景下仍具优势。
|
14天前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
47 4
|
15天前
|
缓存 负载均衡 算法
Linux内核中的进程调度算法解析####
本文深入探讨了Linux操作系统核心组件之一——进程调度器,着重分析了其采用的CFS(完全公平调度器)算法。不同于传统摘要对研究背景、方法、结果和结论的概述,本文摘要将直接揭示CFS算法的核心优势及其在现代多核处理器环境下如何实现高效、公平的资源分配,同时简要提及该算法如何优化系统响应时间和吞吐量,为读者快速构建对Linux进程调度机制的认知框架。 ####
|
1月前
|
机器学习/深度学习 算法 PyTorch
Pytorch-RMSprop算法解析
关注B站【肆十二】,观看更多实战教学视频。本期介绍深度学习中的RMSprop优化算法,通过调整每个参数的学习率来优化模型训练。示例代码使用PyTorch实现,详细解析了RMSprop的参数及其作用。适合初学者了解和实践。
37 1
|
1月前
|
前端开发 算法 JavaScript
无界SaaS模式深度解析:算力算法、链接力、数据确权制度
私域电商的无界SaaS模式涉及后端开发、前端开发、数据库设计、API接口、区块链技术、支付和身份验证系统等多个技术领域。本文通过简化框架和示例代码,指导如何将核心功能转化为技术实现,涵盖用户管理、企业店铺管理、数据流量管理等关键环节。
|
1月前
|
机器学习/深度学习 算法 PyTorch
Pytorch-SGD算法解析
SGD(随机梯度下降)是机器学习中常用的优化算法,特别适用于大数据集和在线学习。与批量梯度下降不同,SGD每次仅使用一个样本来更新模型参数,提高了训练效率。本文介绍了SGD的基本步骤、Python实现及PyTorch中的应用示例。
34 0
|
1月前
|
机器学习/深度学习 传感器 算法
Pytorch-Adam算法解析
肆十二在B站分享深度学习实战教程,本期讲解Adam优化算法。Adam结合了AdaGrad和RMSProp的优点,通过一阶和二阶矩估计,实现自适应学习率,适用于大规模数据和非稳态目标。PyTorch中使用`torch.optim.Adam`轻松配置优化器。
42 0
|
1月前
|
存储 搜索推荐 算法
【排序算法(二)】——冒泡排序、快速排序和归并排序—>深层解析
【排序算法(二)】——冒泡排序、快速排序和归并排序—>深层解析

推荐镜像

更多