最长回文子串问题(Manacher算法)

news2024/11/23 0:50:47

文章目录

    • 题目
    • 1)例子演示
    • 2)思路分析
    • 3)Manacher 算法
    • 4)代码展示

题目

如何求得某字符串 str 的最长回文子串的长度?

要求时间复杂度 O(N)

1)例子演示

什么是回文子串?

回文串即该字符串从前往后读,从后往前读都是一样的,或者说该字符串是对称的,子串表示该字符串在 str 中是连续的一段字符,不能拼接而成

在这里插入图片描述

如上图的字符串 str,"cabbac"是一个回文串,字符串长度为偶数,关于一条虚轴对称,"abbacabba"也是一个回文串,字符串长度为奇数,关于字符 ‘c’ 对称

不难发现该字符串中有很多的回文子串,但是 "abbacabba"便是其中最长的回文子串,其长度 9 就是我们需要返回的结果

2)思路分析

不难想到,求回文子串,我们会让每个字符以其为中心,向两边扩散,如果字符两端的字符相等并且没有超过字符串长度限制就扩,条件不成立了就表示求出了以该字符为中心的回文子串

在这里插入图片描述

如上图所示,我们以字符 ‘b’ 为中心扩散,最后走到了字符串的最左侧,停止扩张,得到了回文子串 “cabac”

如果我们能够将所有字符为中心的回文子串找到,保持记录其中的长度最大值,是不是就能够得到最长回文子串了呢?

但是回文子串中还存在着字符数量为偶数的字符串,为偶数时,是以一条虚轴为对称中心,按照上面的做法就会错过偶数长度的回文子串

为解决这个问题,我们可以将特殊字符插在所有字符间以及字符串两侧,排除奇偶长度字符串的问题,仍然按照上面的思路就能够解决问题

注:特殊字符是什么没有限制,因为特殊字符相当于是一个虚轴的存在,辅助功能,不管算哪个位置的回文子串,特殊字符只可能和特殊字符进行比对,所以不会对原来的字符比对有影响,自然也不会影响到最后的结果

比如对于字符串 “abba”

在这里插入图片描述

在这里插入图片描述

得到的最长回文串的长度为 9,返回的结果就是 9/2 = 4,符合预期

在最差情况下,比如 “ccccccc”,这样的字符串每次在算回文子串时都会扩张到边界才停止,扩张代价 O(N),该字符串长度为 N 时,时间复杂度为 O(N*N)

3)Manacher 算法

该算法能够实现时间复杂度为 O(N),主要原理就在于使得每次扩张时掌握充分的已有信息,在此基础上进行有意义的扩张

在这里有四个比较重要的变量

变量 H:回文半径

变量 D:回文直径

变量 R:之前扩张的所有位置中所到达的最远的回文右边界(初始值为-1)

变量 C:取得更远的回文右边界时,回文中心点(初始值为-1)

比如 “abbac”

在这里插入图片描述

在下标 0 时,此时的回文串为 “#”,回文半径和直径都为1,最右回文边界 R 更新为 0,中心点 C 更新为 0

在下标 1 时,此时的回文串为 “#a#”,回文半径为2,直径为 3,R 更新为 2,C 随之更新为 1

在下标 2 时,此时的回文串为 “#”,回文半径和直径都为1,R、C 都没有更新

注:可以看得出来,R 和 C 是一起更新的

那么在了解了这些变量信息后,我们就可以根据不同的情况来分类,采取不同的求回文子串的措施

情况一:

当前遍历到的字符的位置在最右回文边界外部暴力扩张

就像上面的例子中,index = 0 时,此时 R = -1,字符的位置显而易见在 R 的外部,所以只能进行暴力扩张

情况二:

当前遍历到的字符的位置在最右回文边界及其内部

在第二种情况中又根据当前遍历到的字符的位置 i 关于中心点 C 对称的位置的字符 i’ 的回文区间分成三类

在这里插入图片描述

2.1 第一类:

i' 的回文区间在 L..R 内部

向上面图中就属于这一类,此时 i 位置的字符的回文半径一定和 i' 是一样的

为什么呢?

在这里插入图片描述

如上图所示,区域 ① 是以 i’ 位置的字符为中心的回文子串区域,X ≠ Y,否则回文子串的区域会包含 X 字符 和 Y 字符

因为 i 和 i’ 关于中心点 C 成对称关系,所以区域 ② 也一定是回文子串,并且和区域 ① 呈逆序关系, X = Q,Y = P

所以得出结论 P ≠ Q,区域 ② 就是以 i 位置的字符为中心的回文子串区域

2.2 第二类:

i' 的回文区域有一部分在 L..R 外面

在这里插入图片描述

注:方便起见,去掉了特殊字符

此时以 i 位置的字符为中心的回文子串的回文半径就是 i 到 R 这段距离

这是什么原理呢?

在这里插入图片描述

如上图所示,区域 ① 是以 i’ 位置的字符为中心的回文子串区域中的一部分,X = Y

因为 i 和 i’ 关于中心点 C 成对称关系,所以区域 ② 也一定是回文子串,并且和区域 ① 呈逆序关系,Y = P

由于以 C 为中心点的回文子串区域为 L…R,所以 X ≠ Q

综上所述,X = P,P ≠ Q,区域 ② 就是以 i 位置的字符为中心的回文子串区域

2.3 第三类:

i' 的回文区域边界和 L 重合了

在这里插入图片描述

此时 i 的回文半径至少是 i 到 R,至于会不会更长,就需要在已确定的回文串长度基础上向外比对,向外扩

像上面的这种情况,下标 5 ~ 7 的字符串就不需要进行比对了,一定是回文串,然后在比对 4 位置和 8 位置的字符,直到扩张失败

在这里插入图片描述

如上图所示,区域 ① 是以 i’ 位置的字符为中心的回文子串区域,左边界刚好和 L 重合

因为 i 和 i’ 关于中心点 C 成对称关系,所以区域 ② 也一定是回文子串,并且和区域 ① 呈逆序关系

但是以 i 为中心的回文子串是否会变得更长,还要继续比对 X 字符 和 Y 字符,相等就外扩,不等回文子串就是区域 ②

4)代码展示

//转换方法 "abba"-->{'#','a','#','b','#','b','#','a','#'}
public char[] Transform(String str) {
    char[] chs = str.toCharArray();
    char[] newChs = new char[chs.length*2+1];
    newChs[0] = '#';
    int index = 0;
    int i = 1;
    while (i < newChs.length) {
        newChs[i++] = chs[index++];
        newChs[i++] = '#';
    }
    return newChs;
}
public int Manacher (String str) {
    char[] chs = Transform(str);//转换
    int[] radius = new int[chs.length];//保存每个字符的回文半径
    int R = 0;
    //为了方便,这里的 R 为最右回文边界的后一个字符下标
    //这样 R-i 就刚好是情况二中的第二类的 i 的回文半径
    int C = -1;
    int max = 1;
    for (int i = 0; i < chs.length; i++) {
        //回文半径至少的长度
        // R > i 成立了,就是情况一,回文半径至少是1
        // R > i 不成立,那就是情况二,radius[2*C-i]就是 i' 的回文半径长度,和 R-i 取较小
        radius[i] = R > i ? Math.min(radius[2*C-i],R-i):1;
        //虽然只有情况一和情况二的第三类需要往外扩,但是为了方便,不管哪种情况都扩
        //反正情况二的第一二类情况外扩也会失败的,不影响结果
        //循环进入条件就是左右不可越界
        while (i + radius[i] < chs.length && i - radius[i] > -1) {
            //配对成功,外扩,即回文半径加一
            if (chs[i + radius[i]] == chs[i - radius[i]]) {
                radius[i] ++;
            }else {
                //外扩失败
                break;
            }
        }
        //更新 R 和 C
        if (R < i + radius[i]) {
            R = i + radius[i];
            C = i;
        }
        //保持最大值
        max = Math.max(max,radius[i]);
    }
    //因为有特殊字符的存在,最后的实际最长回文子串的长度是 max-1
    //比如 "#a#b#b#a#",max = 5,返回值为 4
    return max - 1;
}

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

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

相关文章

Stable Diffusion 迁移和部署

文章目录1. 模型概述2. 模型架构3. 模型迁移流程3.1 前置准备3.2 CLIP text encoder3.3 VQ-VAE (fp16)3.4 Text conditioned unet3.5 创建pipeline3.6 web demo部署4. 效果展示5. 相关链接此实验需要最新的sdk实现模型的迁移&#xff0c;最终在1684X上进行推理。代码地址为&…

mysqldumpslow和mysqldumpslow

mysqldumpslow 这是一款mysql自带的慢查询工具&#xff0c;个人使用下来发现&#xff0c;可以满足在很多慢日志中找到有问题的慢的sql&#xff0c;并且可以进行sql排序。属于一种短小精干的工具。 命令大概形式&#xff1a; slowlog是需要分析的日志 mysqldumpslow -s c -t 1…

rocketmq源码-consumer启动

前言 这篇笔记记录consumer启动的逻辑 consumer主要是负责去broker中拉取消息&#xff0c;然后将拉取到的消息&#xff0c;交给消费者去处理 consumer本质上也是一个netty客户端&#xff0c;所以&#xff0c;在启动的时候&#xff0c;和producer有很多相似的点&#xff0c;但是…

[ vulhub漏洞复现篇 ] struts2远程代码执行漏洞s2-052(CVE-2017-9805)

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

[附源码]Node.js计算机毕业设计高校国防教育管理系统Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

基于java+springboot+mybatis+vue+mysql的结合疫情情况的婚恋系统

项目介绍 现实生活中&#xff0c;很多年轻人的社交圈实际上很狭窄&#xff0c;一方面&#xff0c;多元化的社交方式太少了&#xff0c;另一方面&#xff0c;都市人繁忙的生活又让他们很少有空闲去体验传统交友方式&#xff0c;而网络的快捷和多元化恰恰提供了一个好的交友起点…

Blazor组件自做十三: VideoPlayer 视频播放器

Video.js 是一个具有大量功能的流行的视频和音频 JavaScript 库,今天我们试试集成到 Blazor . Blazor VideoPlayer 视频播放器 组件 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VwxxYiKn-1671068849666)(null)] 示例 https://blazor.app1.es/v…

为什么现代企业的ERP系统必须具备CRM?

各行各业的企业都依赖ERP系统和客户关系管理 (CRM) 系统来简化业务运营。企业的销售和运营信息必须实时联系起来&#xff1b;否则&#xff0c;企业的客户活动在现代社会就会变得缓慢或没有效率。 以下业务对象通常被映射在用于销售的CRM系统和用于运营的ERP系统之间&#xff0…

【Processing】我给网友 “战场小包” 做了他的 “自画像”.

前言 突然疫情放开了&#xff0c;在掘金里认识的一个掘友&#xff08;战场小包&#xff09;&#xff0c;突然今天找我。 &#xff1a;寻思啥事呢&#xff0c;原来找我做个自画像。 &#xff1a;行&#xff01;没问题&#xff01; &#xff1a;结果等半天&#xff08;一晚上到今…

rocketmq源码-broker处理consumer拉取消息请求

前言 在前面consumer拉取消息的博客中&#xff0c;有说过&#xff0c;对于consumer&#xff0c;在拉取消息的时候&#xff0c;是需要指定code码的&#xff0c;在consumer去broker拉取消息的时候&#xff0c;指定的code码是&#xff1a;PULL_MESSAGE&#xff0c;所以这篇博客&a…

[附源码]Nodejs计算机毕业设计基于的婚恋系统Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…

APISIX Ingress 如何支持自定义插件

摘要&#xff1a;本篇主要介绍了 Ingress 资源相关的语义&#xff0c;以及如何对 Ingress 资源进行能力的扩展。 作者&#xff1a;张晋涛&#xff0c;API7.ai 云原生技术专家&#xff0c;Apache APISIX PMC 成员&#xff0c;Apache APISIX Ingress Controller 项目维护者。 Ing…

基于C++ 实现简易图书管理系统【100010046】

图书管理系统 基于 C 实现简易图书管理系统 该项目是在学习完 C 语言后&#xff0c;独立完成设计开发的简易图书管理系统 设计的基本要求 基本完成对图书系统的设计&#xff0c;包含基本的功能&#xff0c;无界面设计。 要有明显的分类&#xff0c;对不同的进入者有不同的…

机器学习算法原理归纳总结:回归、聚类、支持向量、推荐、降维与神经网络

机器学习算法原理归纳总结&#xff1a;回归、聚类、支持向量、推荐、降维与神经网络 本文重点参考&#xff1a;唐宇迪博士的课程PPT [特别鸣谢] 完整版资料下载&#xff1a;机器学习算法原理详解代码实战 1.回归算法 2.逻辑回归 3.决策树 决策树实际上是根据样本的特征个数对样…

汇编语言第二章:寄存器

2. 寄存器 寄存器进行信息的存储&#xff0c;对于汇编程序员来说&#xff0c;CPU 中的主要部件是寄存器。8086CPU 有 14 个寄存器&#xff0c;这些寄存器分别是&#xff1a; AX BX CX DX SI DI SP BP IP CS SS DS ES PSW通用寄存器 8086所有的寄存器都是 16 位的&#xff0c…

Android Rust JNI系列教程(二) 创建第一个Rust JNI项目

前言 提到JNI,大家都会想到C,C.不过如今rust又给我们增加了一个选项,借助rust的jni库(https://github.com/jni-rs/jni-rs),我们可以很方便的使Android与rust交互.从本章起,我们将逐步地了解使用rust实现一些经典的jni方法. 创建Rust项目 创建工程 在命令行输入命令: cargo…

超算/先进计算的发展与应用是什么?

经过近十年的快速发展&#xff0c;我国在超算领域的实力已达到世界先进水平。1993年&#xff0c;我国第一台高性能计算机“曙光一号并行机”研制成功&#xff0c;打破了国外IT巨头对我国超算技术的垄断。 自此&#xff0c;我国不断加快超级计算机研制步伐。从全球超级计算机TO…

精华推荐 | 【MySQL技术专题】「主从同步架构」全面详细透析MySQL的三种主从复制(Replication)机制的原理和实战开发(原理+实战)

前提概要 随着应用业务数据不断的增大&#xff0c;应用的响应速度不断下降&#xff0c;在检测过程中我们不难发现大多数的请求都是查询操作。此时&#xff0c;我们可以将数据库扩展成主从复制模式&#xff0c;将读操作和写操作分离开来&#xff0c;多台数据库分摊请求&#xff…

NEUQACM双周赛(三)

目录7-1 打字&#xff08;C&#xff09;题目描述&#xff1a;输入格式:输出格式:输入样例1:输出样例1:输入样例2:输出样例2:解题思路&#xff1a;7-2 分香肠&#xff08;C&#xff0c;最大公约数&#xff09;题目描述&#xff1a;输入格式:输出格式:输入样例:输出样例:解题思路…

节能降耗 | AIRIOT智慧电力综合管理解决方案

电力技术的发展推动各行各业的生产力&#xff0c;与此同时&#xff0c;企业中高能耗设备的应用以及输配电过程中的电能损耗&#xff0c;也在一定程度上加剧了电能供应压力。以工业制造业为例&#xff0c;企业的管理水平、能耗结构、生产组织方式都关系到能源的有效利用率&#…