马拉车算法(Manacher's Algorithm)

马拉车算法(C/C++) 是一种用于在字符串中查找最长回文子串的线性时间复杂度算法。该算法由Udi Manacher在1980年代提出,因此得名。它的核心思想是利用已知的回文信息来减少不必要的比较,从而提高效率。 阅读详情

这是悦乐书的第343次更新,第367篇原创

Manacher’s Algorithm,中文名叫马拉车算法,是一位名叫Manacher的人在1975年提出的一种算法,解决的问题是求最长回文子串,神奇之处在于将算法的时间复杂度精进到了O(N),下面我们来详细介绍下这个算法的思路。

01 算法由来

在求解最长回文子串的问题时,一般的思路是以当前字符为中心,向其左右两边扩展寻找回文,但是这种解法的时间复杂度是O(N^2),那么能不能将时间复杂度再降低一点?做到线性?马拉车算法就完美地解决了这个问题。

02 预处理

回文字符串以其长度来分,可以分为奇回文(其长度为奇数)、偶回文(其长度为偶数),一般情况下需要分两种情况来寻找回文,马拉车算法为了简化这一步,对原始字符串进行了处理,在每一个字符的左右两边都加上特殊字符(肯定不存在于原字符串中的字符),让字符串变成一个奇回文。例如:

原字符串:abba,长度为4
预处理后:#a#b#b#a#,长度为9

原字符串:aba,长度为3
预处理后:#a#b#a#,长度为7

03 计算最长回文子串长度

以字符串"cabbaf"为例,将预处理后的新字符串"#c#a#b#b#a#f#"变成一个字符数组arr,定义一个辅助数组int[] p,p的长度与arr等长,p[i]表示以arr[i]字符为中心的最长回文半径,p[i]=1表示只有arr[i]字符本身是回文子串。

i       0 1 2 3 4 5 6 7 8 9 10 11 12
arr[i]  # c # a # b # b # a #  f  #
p[i]    1 2 1 2 1 2 5 2 1 2 1  2  1

我们来比对分下一下最长回文半径和原字符串之间的关系。在上面例子中,最长回文子串是"#a#b#b#a#",它以arr[6]为中心,半径是5,其代表的原始字符串是"abba",而"abba"的长度为4,可以通过5减去1得到,是字符串"cabbaf"中的最长回文子串,那么我们是不是可以得出最长回文半径和最长回文子串长度之间的关系?

让我们再多看几个例子,如"aba",转换后是"#a#b#a#",以字符'b'为中心的回文,半径是4,减1得到3,3是原字符串的最长回文子串长度。

再例如"effe",转换后是"#e#f#f#e#",以最中间的’#'为中心的回文,半径是5,减1得到4,4是原字符串的最长回文子串长度。

因此,最后我们得到最长回文半径和最长回文子串长度之间的关系:int maxLength = p[i]-1。maxLength表示最长回文子串长度。

04 计算最长回文子串起始索引

知道了最长回文子串的长度,我们还需要知道它的起始索引值,这样才能截取出完整的最长回文子串。

继续以第三步中的字符串"cabbaf"为例,p[6]=5,是最长半径,用6(i)减去最长半径5(p[i])得到1,而1恰好是最长回文子串"abba"的起始索引。

我们再来看一个奇回文的例子。例如"aba",转换后是"#a#b#a#",p[3]=4,最长半径是4,i为3,用i减去4得到-1,数组下标越界了。

在偶回文的情况下,可以满足i减最长半径,而奇回文却会下标越界,我们需要在转换后的字符串前面再加一个字符,解决下标越界的问题,不能是'#',那就加个'$'字符吧,但是加过一个字符后,字符串的长度不是奇数了,只能在尾部再加一个不会重复出现的字符,比如'@',这样字符串的长度依旧是奇数了,满足前面第三部分的条件。

加多一个字符后,奇回文可以正常做减法了,偶回文呢?

i       0 1 2 3 4 5 6 7 8 9 10 11 12 13
arr[i]  $ # c # a # b # b # a  #  f  #
p[i]      1 2 1 2 1 2 5 2 1 2  1  2  1

在补上字符'$'后,p[7]=5,用i减去最长半径,7-5=2,而理想的结果应该是1,那就再除以2吧,这样就能得到1了。而奇回文"aba"在用i减去最长半径后得到的是0,除以2后还是0,可以完美解决下标越界的问题。

结论:最长回文子串的起始索引int index = (i - p[i])/2。

05 计算p数组

在第三步和第四步中我们都用到了一个关键对象p数组,存放的是最长回文子串半径,那么它是怎么来的呢?
还是以上面的例子配合着看,

i       0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
arr[i]  $ # c # a # b # b # a  #  f  #  @ 
p[i]      1 2 1 2 1 2 5 2 1 2  1  2  1

设置两个变量id和mx,id是所有回文子串中,能延伸到最右端位置的那个回文子串的中心点位置,mx是该回文串能延伸到的最右端的位置。

当i等于7时,id等于7,p[id] = 5,在以位置7为中心的回文子串中,该回文子串的右边界是位置12。

当i等于12时,id等于12,p[id] = 2,在以位置12为中心的回文子串中,该回文子串的右边界是位置14。

由此我们可以得出回文子串右边界和其半径之间的关系:mx = p[id]+id。

因为回文字符串是中心对称的,知道中心点位置id,如果一个位置的回文子串以i为中心,并且包含在以id为中心的回文子串中,即mx > i,那么肯定会存在另外一个以j为中心回文子串,和以i为中心的回文子串相等且对称,即p[j] = p[i],而i和j是以id为中心对称,即i+j=2*id,如果知道了i的值,那么j = 2*id - i。

但是我们需要考虑另外一种情况,如果存在一个以i为中心的回文子串,依旧有mx > i,但是以i为中心的回文子串右边界超过了mx,在i到mx的这段回文子串中,与另一端对称的以j为中心的回文子串还是相等的,此时p[i] = mx - i,p[j] = [pi],至于右边界mx之外的子串,即以i为中心的回文子串超出的部分是否还是满足上述条件就需要遍历比较字符了。

因此,在mx > i的情况下,p[i] = Math.min(p[2*id - i], mx - i)。
另外如果i大于mx了,也即是边界mx后面的子串,依旧需要去比较字符计算。

public static String Manacher(String s) {
    if (s.length() < 2) {
        return s;
    }
    // 第一步:预处理,将原字符串转换为新字符串
    String t = "$";
    for (int i=0; i<s.length(); i++) {
        t += "#" + s.charAt(i);
    }
    // 尾部再加上字符@,变为奇数长度字符串
    t += "#@";
    // 第二步:计算数组p、起始索引、最长回文半径
    int n = t.length();
    // p数组
    int[] p = new int[n];
    int id = 0, mx = 0;
    // 最长回文子串的长度
    int maxLength = -1;
    // 最长回文子串的中心位置索引
    int index = 0;
    for (int j=1; j<n-1; j++) {
        // 参看前文第五部分
        p[j] = mx > j ? Math.min(p[2*id-j], mx-j) : 1;
        // 向左右两边延伸,扩展右边界
        while (t.charAt(j+p[j]) == t.charAt(j-p[j])) {
            p[j]++;
        }
        // 如果回文子串的右边界超过了mx,则需要更新mx和id的值
        if (mx < p[j] + j) {
            mx = p[j] + j;
            id = j;
        }
        // 如果回文子串的长度大于maxLength,则更新maxLength和index的值
        if (maxLength < p[j] - 1) {
            // 参看前文第三部分
            maxLength = p[j] - 1;
            index = j;
        }
    }
    // 第三步:截取字符串,输出结果
    // 起始索引的计算参看前文第四部分
    int start = (index-maxLength)/2;
    return s.substring(start, start + maxLength);
}

06 小结

马拉车算法将求解最长回文子串的时间复杂度降低到了O(N),虽然也牺牲了部分空间,其空间复杂度为O(N),但是其算法的巧妙之处还是值得学习和借鉴的。

算法专题目前已连续日更超过六个月,算法题文章211+篇,公众号对话框回复【数据结构与算法】、【算法】、【数据结构】中的任一关键词,获取系列文章合集。

以上就是全部内容,如果大家有什么好的解法思路、建议或者其他问题,可以下方留言交流,好看、留言、转发就是对我最大的回报和支持!

使用VIVADO 2019.1 搭建以太网接口 以太网 MAC+SGMII->GMII接口转换 以FPGA V7为例,搭建一个千兆以太网接口。运行环境为Vivado2019.1。板卡的FPGA V7与千兆网口芯片采用SGMII互联,最终实现FPGA与上位机之间的千兆网卡通信,实际实现时,V7采用MAC + GMII ->SGMII的结构。SGMII接口使用1G/2.5G Ethernet PCS/PMA or SGMII ip核,MAC层使用Tri Mode Ethernet MAC ip核。 由于MAC层IP不支持SGMII接口,因此需要G 阅读详情

相关推荐

ESP32Servo库驱动SG90舵机:从入门到精准控制

本文详细介绍了如何使用ESP32Servo库驱动SG90舵机,从硬件连接到软件环境搭建,再到编写示例程序实现精准控制。内容涵盖库安装、基础驱动代码解析、舵机校准方法以及平滑运动控制,帮助开发者快速掌握ESP32控制舵机的核心技能,并提供了电源管理、信号抗干扰等实用避坑指南。

ik678901的博客 895

Manacher (马拉车算法)

Manacher (马拉车算法) 的介绍 中心扩展算法 —— 优化 —— 马拉车 P3805 模板manacher 算法 P1659 [国家集训队]拉拉队排练 P4555 [国家集训队]最长双回文串

日常记录,挨打要立正! 2万+

药物不良反应预测实战指南:从FAERS数据挖掘到PyTorch深度学习模型(含可解释性分析)

【代码】药物不良反应预测实战指南:从FAERS数据挖掘到PyTorch深度学习模型(含可解释性分析)

理论都是虚的,代码才是王道。 721

Manacher算法(马拉车算法)

Manacher算法,又叫“马拉车”算法,可以在时间复杂度为O(n)的情况下求解一个字符串的最长回文子串长度的问题。

诸君拔剑吧. 4280

解决TexStudio编写LaTeX论文引用文献问题:正文引用处出现[?],参考文献也不显现

当我们利用LaTeX编写论文时,可能会出现引用参考文献时正文引用处出现?号,肯能的原因及解决方案如下: 问题一 忘记在\end{document}前面放\bibliographystyle{样式}和\bibliography{参考文献bib文件名}: \bibliographystyle{plain} \bibliography{sample} 其中, LaTeX 标准选项及其样式共有以下8种: plain,按字母的顺序排列,比较次序为作者、年度和标题. unsrt,样式同plain,只是按照引用的先后

左手の明天的博客 9528

2025华为OD机试真题目录汇总 B+C+D+E卷(570道)(Python语言)

本文是华为OD机试真题(Python 语言)专栏的目录贴(持续更新中…)华为OD是Outsourcing Dispacth模式,是华为和外企德科联合招聘的简称。目前华为大多数是OD招聘。OD模式也是华为提出的一种新的用工形式,每年都会从OD项目挑优秀员工转为正编。所有OD和正式员工一样对待,没有差别,部门平时开会、评审等活动都是一起参加的,是同薪同酬,都是由华为的HR直接定的薪资,看的是我们技术能力。华为是大企业,能到华为上班也是有能力的,每个部门都有好多职位,是金子,在哪都会发光。

KJ.JK 18万+

马拉车(Manacher)算法

马拉车算法与KMP算法都是解决字符串问题的经典算法,为什么把它们俩个放在一起呢?这是因为它们的思想都是用前面已知的信息去更新后面的信息。

qq_42376883的博客 1570

软件可靠性设计

本文系统介绍了软件可靠性设计与建模的理论框架和实施方法。首先从可靠性定义、核心思想和与建模的关系等理论基础出发,重点阐述了模块化设计、冗余容错、异常处理等六类可靠性设计方法及其技术栈。文章详细分析了可靠性设计在软件生命周期各阶段的应用,并深入探讨了设计与建模的深度结合机制,包括参数影响、验证方法和量化指标。最后提出了覆盖设计、开发、测试和运行四个阶段的落地策略,形成完整的"设计-建模-运行"流程体系。全文强调通过预防性设计和量化建模相结合的方法提升软件系统的可靠性和稳定性。

moton2017的博客 896

【算法】——Manacher Algorithm(马拉车算法)

一、马拉车算法来源 马拉车算法Manacher‘s Algorithm是用来查找一个字符串的最长回文子串的线性方法,由一个叫Manacher的人在1975年发明的,这个方法的最大贡献是在于将时间复杂度提升到了线性,这是非常了不起的。对于回文串想必大家都不陌生,就是正读反读都一样的字符串,比如 "bob", "level", "noon" 等等,那么如何在一个字符串中找出最长回文子...

汤庆 8404

【即插即用模块】Transformer篇 | TCSVT 2025 | DAFM:双注意力聚合+反向跨模态筛选,特征精准捕捉并融合!

本文提出了一种双模态注意力融合模块(DAFM),用于增强多模态图像融合任务中的特征交互。该模块通过双注意力聚合机制,结合自注意力与跨模态注意力,有效提升特征融合效果。DAFM采用可学习平衡因子动态调整注意力权重,并运用深度卷积降低计算复杂度。实验结果表明,该模块能显著提升多模态图像融合质量,适用于RGB-红外等双模态视觉任务。模块代码实现简洁,可直接集成到现有网络中作为特征融合层使用。

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 175

手把手教您国内安装 Claude Code 并配置自定义大模型(包含New-API中转和可视化管理工具的使用)

国内免魔法畅用 Claude Code!手把手教你 Windows 安装、配置文件写法、对接阿里百炼 / DeepSeek / 智谱✨,附完整参数、正确命令、三方工具管理方案,新手一次成功!✅

XiaoqiangClub的博客 3782

[杂谈] 11. Manacher's Algorithm 马拉车算法

1. 算法由来 马拉车算法 Manacher‘s Algorithm 是用来查找一个字符串的最长回文子串的线性方法,由一个叫 Manacher 的人在 1975 年发明的,这个方法的最大贡献是在于将时间复杂度提升到了线性,这是非常了不起的。 对于回文串想必大家都不陌生,就是正读反读都一样的字符串,比如 “bob”, “level”, “noon” 等等,那么如何在一个字符串中找出最长回文子串呢,可...

Y-puyu 的博客 1621

闲鱼爬虫实战:模拟手机端破解反爬策略,爬取指定商品搜索数据,爬取闲鱼搜索指定商品(需手机端模拟)o 技术点:抓包分析、cookie与token

技术点作用抓取手机 App 请求,分析 API 结构、Header、参数加密情况requestsPython 中最常用的 HTTP 库,用于发送模拟请求Session 对象自动管理 Cookie,保持会话User-Agent & 设备特征模拟手机型号、系统版本,绕过服务端检测闲鱼部分接口存在签名,需要逆向或复用有效 Token多线程/异步提高爬取效率(需节制,避免封 IP)解析 HTML 或 JSON 数据注意:闲鱼的接口加密程度中等偏上,部分接口存在动态签名(如x-signx-mini-wua。

2201_76125261的博客 712

Manacher's Algorithm 马拉车算法

转载自https://www.cnblogs.com/grandyang/p/4475985.html 这个马拉车算法Manacher‘s Algorithm是用来查找一个字符串的最长回文子串的线性方法,由一个叫Manacher的人在1975年发明的,这个方法的最大贡献是在于将时间复杂度提升到了线性,这是非常了不起的。对于回文串想必大家都不陌生,就是正读反读都一样的字符串,比如 "bob...

baymax520的博客 271

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

马拉车算法(Manacher's Algorithm)

马拉车算法 这个马拉车算法 Manacher‘s Algorithm 是用来查找一个字符串的最长回文子串的线性方法,由一个叫 Manacher 的人在 1975 年发明的,这个方法的最大贡献是在于将时间复杂度提升到了线性,这是非常了不起的。对于回文串想必大家都不陌生,就是正读反读都一样的字符串,比如 “bob”, “level”, “noon” 等等,那么如何在一个字符串中找出最长回文子串呢,可以...

qq_36134437的博客 442

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Manacher's Algorithm 马拉车算法(C++)

Manacher’s Algorithm 马拉车算法 什么鬼名字? 参考以下链接,把java代码用c++写了一遍 最长回文字符串算法-Manacher’s Algorithm-马拉车算法 算法介绍 这个马拉车算法Manacher‘s Algorithm是用来查找一个字符串的最长回文子串的线性方法,由一个叫Manacher的人在1975年发明的,这个方法的最大贡献是在于将时间复杂度提升到了线性,这...

CWZgxu 1044

理解 Manacher's Algorithm(马拉车算法)——最长回文子串问题

求字符串 s 的最长回文子串 一、马拉车算法(Manacher’s Algorithm) 通常的回文串匹配方法:以某个字符为中心,依次向两边匹配。这样有一个问题是,当回文串长度分别为奇数和偶数时,不方便统一处理。马拉车算法的核心思想是利用已经计算过的回文串,并且根据对称性减少匹配次数。另外对字符串进行特殊处理,消除回文串长度奇偶性可能造成的影响。 1. 改造字符串 在字符串 S的相邻字符之间、字符...

charlsonzhao的博客 662

Manacher's Algorithm(马拉车算法)

ManacherAlgorithm算法,俗称马拉车算法,其时间复杂为O(n)。该算法是利用回文串的特性来避免重复计算的,至于如何利用,且由后面慢慢道来。 在时间复杂度为O(n^2)的算法中,我们在遍历的过程要考虑到回文串长度的奇偶性,比如说“abba”的长度为偶数,“abcba”的长度为奇数,这样在寻找最长回文子串的过程要分别考奇偶的情况,是否可以统一处理了? 一)第一步是改造字符...

weixin_30299539的博客 238
上一篇: LeetCode.5-最长回文子串(Longest Palindromic Substring)
下一篇: LeetCode.893-特殊相等字符串组(Groups of Special-Equivalent Strings)
程序员小川
博客等级 码龄10年 47粉丝 305原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值