Manacher算法

news/2024/5/9 7:35:37/文章来源:https://blog.csdn.net/u011386173/article/details/128317036

0、概括

  • Manacher算法用于求解字符串中最长回文子串问题。

  • Manacher算法的核心:

    1. 理解回文半径数组;
    2. 理解所有中心的回文最右边界 R,和取得 R 时的中心点 C;
    3. 理解 L...(i')...C...(i)...R 的结构,以及根据 i′i'i 回文长度进行的状况划分
    4. 每一种情况划分,都可以加速求解 iii 回文半径的过程
  • 补充:子串一定是连续的,如 abc123321def,最长回文子串就是 123321

  • 实际可用于DNA序列的研究。

1、引入

假设字符串 str 长度为 NNN,想返回最长回文子串的长度,且要求时间复杂度为 O(N)O(N)O(N)

2、暴力解找最长回文子串

【方法一】

直接以每个位置的字符作为中心点往两侧扩,比较左右两侧的字符是否相等,如果相等继续比较两侧的下一个字符,但是这种方法对于偶数长度的回文子串不适用。

【方法二】

那怎么办呢?处理原始字符串:最左边添加一个 #,每两个字符之间都添加一个 #,最后一个字符后也添加#

如原始字符串为 121aaaa232aa,处理后的字符串变为 #1#2#1#a#a#a#a#2#3#2#a#a#

好处:将处理后的字符串的每个字符位置作为中心往两侧扩,得到的最大长度除以 2,就能得到原始串的一个回文长度。

问:如果将原始串处理成新串的时候不用字符 #,而是用一个原始串中已经出现过的字符,会不会形成干扰?

答:不会,因为以任何位置为中心往两边扩的时候,都是实际的字符在比较,新加的字符在比较,不会存在实际字符和新加字符比较的情况,所以这个新加的字符是什么都可以。

时间复杂度:以 aaaaa 字符串为例,处理后的字符串 #a#a#a#a#a#,以每个位置为中心往两边扩需要比较的次数依次为:0 + 1 + 2 + 3 + 4 + 5 + 4 + 3 + 2 + 1 + 0,以字符串的中间位置为轴,左右两边都是等差数列,所以总的时间复杂度是 O(n2)O(n^2)O(n2)

之所以暴力解的过程慢,是因为以每个位置为中心往两侧扩的操作都是独立的,也就是之前的操作对于之后的操作没有任何指导意义。

3、Manacher算法找最长回文子串

Manacher算法在一个长度为 NNN 的字符串中找到最长回文子串的时间复杂度为 O(N)O(N)O(N)

基础概念

  • 回文直径 和 回文半径

abc12321def:回文串为“12321”,回文直径为5,回文半径为3

1221: 回文串为“1221”,回文直径为4,回文半径为2

  • 回文半径数组

记录从左往右以每个位置为中心向左右两边扩的长度

  • 最右回文边界

整型变量,用 R 表示,初始时R = -1。

图解R:
请添加图片描述请添加图片描述

不管是以哪个位置为中心扩的,只要让这个回文区域的右边界变得更右了,R就记录这个更往右的位置。

  • 最右回文边界的中心

CCC 变量表示,记录是哪个中心点使得R往右扩的。如上图解中,C依次为0、1、2、3、5,R不更新C就不会更新,R一旦更新,C一定会跟着更新。

流程

iii 位置为中心向左右两边扩,可能会遇到的情况:

  1. iii 没有被 RRR 罩住(iii > RRR),则不优化,直接暴力扩;
  2. iiiRRR 罩住(i<=Ri<=Ri<=R),可以优化。这时候 CCCiii 左边, RRRiii 右边,以 CCC 为中心点,一定能找到 i′i'i,以及RRR 的对称点 LLL,如果 iiiRRR 罩住一定存在这种拓扑关系:
    在这里插入图片描述
    然后根据 i′i'i 能扩多大的区域的信息来分类:
    • case1 : i′i'i 扩出的区域在 (L,R)(L,R)(L,R)
      请添加图片描述
      请添加图片描述
    • case 2i′i'i的回文区域在 [L,R][L,R][L,R] 之外
      请添加图片描述请添加图片描述
    • case3i′i'i为中心的的回文区域左边界和 LLL 压线请添加图片描述

整理各种情况,可得伪代码:

1)i > R, 无优化,暴力扩
2)i <= R:① i’扩充的区域在(L, R) 内,直接得到答案和i'的回文区域一样,不用扩,O(1)② i'扩充的区域左边界 < L,右边界 < R, 即不完全在(L,R)范围内,也不用扩,回文半径为 i 到 R 的距离,也是直接得到答案,O(1)③ i'扩充的区域左边界 = L,右边界 < R, i 到 R 这段为半径的区域不需要验证,再往右的位置对应左边继续往下验

复杂度分析:任何一个位置,都可能会失败一次(遇到不匹配的或没有字符了),失败的总次数 NNN 次;如果匹配成功,一定会使得 RRR 变大,2) 的 ①② 本来就是 O(1)O(1)O(1) 复杂度,不需要再讨论;而 2) 的 ③ 情况 RRR 内部不验,RRR 外部继续往右的时候,如果失败了就1次,如果成功依然会使得 RRR 变大。而 RRR 的变化有极限(000~NNN),且整个方法中,只要匹配成功,RRR 就增大,RRR 是不回退的,所以整个方法的时间复杂度O(N)O(N)O(N)

4、Manacher算法代码实现

public class Manacher {//返回最长回文子串的长度public static int manacher(String s) {if (s == null || s.length() == 0) {return 0;}//1. 处理字符串,原始字符串处理为manacher字符串,即给前后和字符间的位置添加#//例:"12132" -> "#1#2#1#3#2#"char[] str = manacherString(s); // 回文半径数组,该数组中的最大值/2就是结果int[] pArr = new int[str.length];int C = -1;// 讲解时:R代表最右的扩成功的位置// coding:最右的扩成功位置的,再下一个位置,即失败的位置int R = -1;int max = Integer.MIN_VALUE;//2. 以每个位置为中心点进行往左右扩的操作for (int i = 0; i < str.length; i++) { // 0 1 2// R第一个违规的位置,i<R 时,i才在R(扩成功的区域,即讲解时的R)内部;而i>=R时,i在R(扩成功的区域,即讲解时的R)外部// 如果i在R外(i>=R) ,以i为中心的回文半径至少是1(它自己);// 如果i在R内(i<R),就要用之前讨论的三种情况细分(根据i’的扩的区域):// 1)i’扩的区域在(L,R)内,i扩的区域 = i‘扩的区域// 2)i'扩的区域在[L,R]外,i扩的区域:i到R的距离为回文半径// 3)i'扩的区域左边界在L,i扩的区域:至少i到R的距离不用验// 下面这句代码就可以完成:// 2 * C - i 就是i',R - i 是i到R的距离,意思就是i‘的回文半径长度和R到i的距离,谁小就是至少不用验的区域// 这句代码的结果就是哪个区域不用验,也就是i位置扩出来的答案,i位置扩的区域,至少是多大。pArr[i] = R > i ? Math.min(pArr[2 * C - i], R - i) : 1;//不用验的区域的左右两侧的字符进行比较,就是往外扩的过程//上述的三种情况中,不用验的两种情况1)和2)在第一次进入这个循环就会breakwhile (i + pArr[i] < str.length && i - pArr[i] > -1) {if (str[i + pArr[i]] == str[i - pArr[i]])pArr[i]++;else {break;}}//更新R和C的值if (i + pArr[i] > R) {R = i + pArr[i];C = i;}//max最后记录的是最大回文半径,而且是处理后的字符串的回文半径//如121,处理后#1#2#1#,回文半径为4,4-1=3就是原字符串的最大回文串长度//偶回文同样1221,处理后#1#2#2#1#,回文半径为5,  5-1=4就是原字符串的最大回文长度max = Math.max(max, pArr[i]); }return max - 1;}public static char[] manacherString(String str) {char[] charArr = str.toCharArray();char[] res = new char[str.length() * 2 + 1];int index = 0;for (int i = 0; i != res.length; i++) {res[i] = (i & 1) == 0 ? '#' : charArr[index++];}return res;}// for testpublic static int right(String s) {if (s == null || s.length() == 0) {return 0;}char[] str = manacherString(s);int max = 0;for (int i = 0; i < str.length; i++) {int L = i - 1;int R = i + 1;while (L >= 0 && R < str.length && str[L] == str[R]) {L--;R++;}max = Math.max(max, R - L - 1);}return max / 2;}// for testpublic static String getRandomString(int possibilities, int size) {char[] ans = new char[(int) (Math.random() * size) + 1];for (int i = 0; i < ans.length; i++) {ans[i] = (char) ((int) (Math.random() * possibilities) + 'a');}return String.valueOf(ans);}public static void main(String[] args) {int possibilities = 5;int strSize = 20;int testTimes = 5000000;System.out.println("test begin");for (int i = 0; i < testTimes; i++) {String str = getRandomString(possibilities, strSize);if (manacher(str) != right(str)) {System.out.println("Oops!");}}System.out.println("test finish");}
}

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mac环境编译安装tesseract-4.1.1

Mojave 编译安装 tesseract-4.1.1 前言 顺便学习下Python&#xff0c;尝试使用Python3安装tesseract进行文字识别&#xff0c;结果踩了好深一个坑&#xff0c;特此记念…… 好多坑&#xff0c;好多坑…… 参考彭世瑜的这一篇&#xff1a;https://blog.csdn.net/mouday/arti…

BEVFormer-accelerate:基于 EasyCV 加速 BEVFormer

导言 BEVFormer是一种纯视觉的自动驾驶感知算法&#xff0c;通过融合环视相机图像的空间和时序特征显式的生成具有强表征能力的BEV特征&#xff0c;并应用于下游3D检测、分割等任务&#xff0c;取得了SOTA的结果。我们在EasyCV开源框架&#xff08;https://github.com/alibaba…

KEIL5软件仿真支持的器件

问题的提出 用KEIL进行软件仿真&#xff0c;想观察一下处理器STM32F091RCY的I2C和DAC引脚输出的波形&#xff0c;发现无法向波形中添加信号&#xff0c;如下图所示 当在命令行中输入 dir vtreg 指令时&#xff0c;仅仅能够显示内核的寄存器&#xff0c;外设的寄存器无法输出&a…

大咖说|云端即时渲染:下一代互联网的算力基座?

阿里云【大咖说】子系列【计算讲谈社】第十五讲播出&#xff01; 下一代互联网是什么&#xff1f;其算力基座又是什么&#xff1f; 14:00-15:30 全网播出&#xff1a;【计算讲谈社】第十五讲&#xff0c;蔚领时代创始人兼CEO郭建君、蔚领时代数字人事业部总经理费元华、蔚领时…

前端基础(十一)_Float浮动、清除浮动的几种方法

浮动 1、什么是浮动&#xff1f; 目的&#xff1a;为了让多个块级元素在同一行显示&#xff1b; 文档流&#xff1a;可显示的对象在排列时所占的位置&#xff1b; 浮动&#xff1a;使元素脱离正常的文档流&#xff0c;按照指定的顺序&#xff0c;方向发生移动&#xff0c;直到…

让人恶心的多线程代码,性能怎么优化!

Java 中最烦人的&#xff0c;就是多线程&#xff0c;一不小心&#xff0c;代码写的比单线程还慢&#xff0c;这就让人非常尴尬。 通常情况下&#xff0c;我们会使用 ThreadLocal 实现线程封闭&#xff0c;比如避免 SimpleDateFormat 在并发环境下所引起的一些不一致情况。其实…

深度学习目标检测:YOLOv5实现红绿灯检测(含红绿灯数据集+训练代码)

深度学习目标检测&#xff1a;YOLOv5实现红绿灯检测(含红绿灯数据集训练代码) 1. 前言 本篇博客&#xff0c;我们将手把手教你搭建一个基于YOLOv5的红绿灯目标检测项目。目前&#xff0c;基于YOLOv5s的红绿灯检测精度平均值mAP_0.50.93919&#xff0c;mAP_0.5:0.950.63967&…

【数据结构-JAVA】ArrayList

目录 1. 线性表 2. 顺序表(ArrayList) 2.1 什么是顺序表&#xff1f; 2.2 顺序表的使用 2.2.1 ArrayList 的构造方法 2.2.2 ArrayList 的常规操作 2.2.3 ArrayList 的遍历 2.3 顺序表的优缺点 3. 练习题 3.1 练习1 一道面试题 3.2 练习2 杨辉三角形 3.3 练习3 洗牌算法 3.4 …

PreSTU:一个专门为场景文本理解而设计的简单预训练模型

摘要&#xff1a;在视觉与语言&#xff08;V&L&#xff09;模型中&#xff0c;阅读和推理图像中的文本的能力往往是缺乏的。我们如何才能学习出强大的场景文本理解&#xff08;STU&#xff09;的V&L模型呢&#xff1f;本文分享自华为云社区《场景文本理解预训练PreSTU》…

如何进行系统设计

文章目录1. 理解需求1.1 功能性需求1.2 非功能性需求2. 系统设计3. Api设计4. 数据模型设计5. 高可用、高性能、可监控等数据密集型应用设计凤凰架构 重点&#xff1a;自己整理的非权威&#xff0c;不具代表性&#xff0c;自己去取舍哈。 1. 理解需求 1.1 功能性需求 解决什么…

深度学习 Day22——利用LSTM实现火灾温度预测

深度学习 Day22——利用LSTM实现火灾温度预测 文章目录深度学习 Day22——利用LSTM实现火灾温度预测一、前言二、我的环境三、LSTM介绍1、长期依赖的问题2、LSTM3、LSTM结构四、前期工作1、设置GPU2、导入数据3、数据可视化五、构建数据集1、设置X、y2、设置归一化3、划分数据集…

宽凳科技完成超亿元B1轮融资 率先突破高精地图量产落地

近日&#xff0c;国内领先的高精地图及其智能应用综合解决方案服务商宽凳科技宣布完成B1轮超亿元融资。本轮融资由聚焦于新能源汽车产业链投资及新兴技术产业投资的紫峰资本与信益资本联合领投&#xff0c;崇业投资跟投&#xff0c;同时本轮资本引入了德清政府战略投资&#xf…

Vue3 —— 使用Vite配置环境变量

文章目录 一、为什么要配置环境变量?二、在Vite中配置环境变量 1.环境变量和模式2.环境变量3.生产环境替换4.env 文件总结一、为什么要配置环境变量? 在一个产品的前端开发过程中&#xff0c;一般来说会经历本地开发、测试脚本、开发自测、测试环境、预上线环境&#xff0c;然…

如何计算香港服务器公网带宽的实际下载速度?

如何计算香港服务器公网带宽的实际下载速度?下面分享香港服务器带宽实际下载速度对照表及计算方法&#xff1a; 香港服务器带宽实际下载速度计算方法 香港服务器以1Mbps公网带宽为例&#xff0c;香港服务器1M带宽实际下载速度峰值128KB/S&#xff0c;为什么不是1M/S&#xff0…

educoder:实验13 算法-穷举法和二分法

第1关&#xff1a;百钱百鸡 任务描述 我国古代数学家张丘建在《算经》一书中提出的数学问题&#xff1a;鸡翁一值钱五&#xff0c;鸡母一值钱三&#xff0c;鸡雏三值钱一。百钱买百鸡&#xff0c;问鸡翁、鸡母、鸡雏各几何&#xff1f; 相关知识 为了完成本关任务&#xff…

《纳瓦尔宝典》笔记三——做自己真正感兴趣的事情

你合上书本&#xff0c;留在你脑子里的才真正是你的智慧 目录 一、开始让你兴致盎然&#xff0c;后来又让你觉得索然无味了吗 二、在“成为自己”这件事“上&#xff0c;没有人比你做得好 三、专长无法被教授&#xff0c;但可以被学习 四、上学能带来什么 五、尽量做不需…

OM6621系列国产M4F内核低功耗BLE5.1大内存SoC蓝牙芯片

目录OM6621系列简介OM6621P系列芯片特性应用领域OM6621系列简介 随着5G与物联网时代的到来&#xff0c;智慧城市、电动出行、智能家居、可穿戴设备等应用高速发展&#xff0c;低功耗蓝牙技术在近几年智能化浪潮中的地位也尤为重要。OM6621系列的开发即是为解决国内低功耗蓝牙应…

[整型/浮点型二分算法详解]二分查找算法真的很简单吗

&#x1f3d6;️作者&#xff1a;malloc不出对象 ⛺专栏&#xff1a;《初识C语言》 &#x1f466;个人简介&#xff1a;一名双非本科院校大二在读的科班编程菜鸟&#xff0c;努力编程只为赶上各位大佬的步伐&#x1f648;&#x1f648; 目录前言一、二分查找是什么二、二分查找…

Linux操作系统的安全合规性检查和加固

1. 账号和口令 1.1 禁用或删除无用账号 减少系统无用账号&#xff0c;降低安全风险。 操作步骤 使用命令 userdel 删除不必要的账号。 使用命令 passwd -l 锁定不必要的账号。 使用命令 passwd -u 解锁必要的账号。 1.2 检查特殊账号 检查是否存在空口令和root权限的账号…

DSPE-PEG-N3,磷脂-聚乙二醇-叠氮 点击化学PEG试剂,可用于药物传递、基因转染和生物分子修饰

中文名称 叠氮聚乙二醇磷脂、磷脂聚乙二醇叠氮 简称 N3-PEG-DSPE、DSPE-PEG-N3 物理性质&#xff1a;米白色/白色固体或粘性液体取决于分子量。 溶剂&#xff1a; 溶于大部分有机溶剂&#xff0c;和水有很好的溶解性。 活性基团&#xff1a; N3 反应基…