Manacher 算法——Leetcode 5.最长回文子串

news2024/11/16 7:21:57

在了解之前,我们先要了解什么是回文串,什么是回文子串。

回文串和回文子串:

        回文串是指一个字符串正序遍历和反向遍历结果相同的字符串。如 ABBA,正着读反着读结果是一样的。

有了回文串的概念,回文子串的概念也就显而易见了。也就是一个字符串的子字符串是回文串,则该子字符串被称为回文子串。

有了这些概念,我们就能介绍Manacher算法。在我们看实际例题前,我们先来看看什么是Manacher算法。

Manacher算法介绍:

Manacher算法是一个用来查找一个字符串中的最长回文子串(不是最长回文序列)的线性算法。它的优点就是把时间复杂度为O(n^2)的暴力算法优化到了O(n)。

既然是一个寻找字符串的最长回文子串,我们就能想到一种暴力的解法:通过从一个字符串中每个字符的位置开始,向两边扩展,一次一位。当左右指针相对应的字符相同时,那么我们就初步得到了一个回文子串。当我们对整个字符串都进行这样的操作时,我们就能得到最长的回文子串。

这样做的时间复杂度是O(n^2),也是我们想要优化的对象。

下面放出例题和我们通过暴力的解法的代码:

例题:

给你一个字符串 s,找到 s 中最长的回文子串。

如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。

示例 1:

输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。

示例 2:

输入:s = "cbbd"
输出:"bb"

提示:

  • 1 <= s.length <= 1000
  • s 仅由数字和英文字母组成
代码:
class Solution {
    public String longestPalindrome(String s) {
        int len = s.length();
        String max = s.substring(0,1);
        for(int i=0;i<len-1;i++){
            String odd = new String();
            String even = new String();
            if(s.charAt(i)==s.charAt(i+1)) even = extend(s,i,i+1);
            odd = extend(s,i,i);
            if(odd.length() > max.length()) max = odd;
            if(even.length() > max.length()) max = even;
        }
        return max;
    }
    public String extend(String s,int l,int r){
        int len = s.length();
        String ans = new String();
        while(l>=0 && r<s.length()){
            if(s.charAt(l) == s.charAt(r)){
                if(l==r) ans+=s.charAt(l);
                else{
                    ans = s.charAt(l)+ans;
                    ans = ans + s.charAt(r);
                }
                l--;
                r++;
            }
            else{
                break;
            }
        }
        return ans;
    }
}

当我们遇到ABA这样的字符串是,我们直接扩展得到的结果是正确的131。但是当遇到ABBA这样的偶数长度是,我们得到的结果却是1111,显然这样是错误的。因此,我们对此专门做了分类讨论:

当此处的字符与下一个相同时,我们扩展是从这两个同时扩展的。也就是解决了偶数的问题。‘

当此处的字符与下一个不相同的时候,我们将会从当前的字符直接开始扩展,也就是奇数的问题。

这样做虽然解决了找寻最长回文子串的问题,但是我们会发现每次向外扩展就是O(N)的时间复杂度。每次从一个字符开始向外扩展,也就是一共花费O(N^2)的时间复杂度。

那么有没有更好的方法呢?下面来介绍Manacher算法

Manacher算法:

首先我们要解决的是将麻烦的奇偶情况分类讨论的问题。我们显然可知,偶数的情况相较于奇数的情况更加复杂,因为奇数只需要从此处开始向两边扩展即可。因此我们要想办法将偶数串变成奇数串,同时不影响判断。所以我们需要预处理。

预处理:

假设一个字符串长度为n,无论n是奇是偶,2*n+1的结果一定是奇数,这是毋庸置疑的。所以我们只需将原字符串长度变成2n+1即可。在不影响原串的前提下,我们需要插入n+1个无关字符。插在哪里不影响呢?当然是间隙之间。比如插入'*' ,'$' ,'#' 这种无关字符。这样一来,我们就得到了一个奇数长度的字符串。

Manacher算法核心:

为了介绍Manacher算法,我们在此需要引入几个概念:

Manacher字符串:经过Manacher预处理的字符串
回文半径:回文字符串的中心字符到两端距离。

最右回文边界R:在遍历字符串时,每个字符遍历出的最长回文子串都会有个右边界,而R则是所有已知右边界中最靠右的位置,也就是说R的值是只增不减的。

回文中心C:取得当前R的第一次更新时的回文中心。由此可见R和C时伴生的。

半径数组:这个数组记录了原字符串中每一个字符对应的最长回文半径。

## 此处因为Manacher字符串长度是 2n+1 ,因此它的半径数组记录的其实是原字符串回文子串的回文直径,也就是回文子串的长度。

初始化 R C均为-1,然后从0处遍历字符串。同时创建半径数组。这里有点与概念相差的小偏差,就是R实际是最右边界位置的右一位。因为在计算时R包含了C的位置,导致pArr[i]+i重复计算了一次i的位置。

这时会遇到三种情况:

此处参考了 https://www.cnblogs.com/cloudplankroader/p/10988844.html 的图片和思想。

1️⃣ i > R ,也就是i在R外,此时没有什么花里胡哨的方法,直接暴力匹配,此时记得看看C和R要不要更新。

2️⃣ i <= R,也就是i在R内,此时分三种情况,在讨论这三个情况前,我们先构建一个模型

L是当前R关于C的对称点,i'是i关于C的对称点,可知 i' = 2*C - i,并且我们会发现,i'的回文区域是我们已经求过的,从这里我们就可以开始判断是不是可以进行加速处理了

情况1:i'的回文区域在L-R的内部,此时i的回文直径与 i' 相同,我们可以直接得到i的回文半径,下面给出证明

红线部分是 i' 的回文区域,因为整个L-R就是一个回文串,回文中心是C,所以i形成的回文区域和i'形成的回文区域是关于C对称的。

情况2:i'的回文区域左边界超过了L,此时i的回文半径则是i到R,下面给出证明

 

首先我们设L点关于i'对称的点为L',R点关于i点对称的点为R',L的前一个字符为x,L’的后一个字符为y,k和z同理,此时我们知道L - L'是i'回文区域内的一段回文串,故可知R’ - R也是回文串,因为L - R是一个大回文串。所以我们得到了一系列关系,x = y,y = k,x != z,所以 k != z。这样就可以验证出i点的回文半径是i - R。

情况3:i' 的回文区域左边界恰好和L重合,此时i的回文半径最少是i到R,回文区域从R继续向外部匹配,下面给出证明

因为 i' 的回文左边界和L重合,所以已知的i的回文半径就和i'的一样了,我们设i的回文区域右边界的下一个字符是y,i的回文区域左边界的上一个字符是x,现在我们只需要从x和y的位置开始暴力匹配,看是否能把i的回文区域扩大即可。

总结一下,Manacher算法的具体流程就是先匹配 -> 通过判断i与R的关系进行不同的分支操作 -> 继续遍历直到遍历完整个字符串

 简单来说,我们需要在暴力算法的基础上利用到以前的结果。如果i<R,那么说明还在范围内,由之前的结果作参考,遍历的范围小。否则只能从1开始逐步遍历。

class Solution {
    public String longestPalindrome(String s) {
        int len = s.length();
        char[] tchs = s.toCharArray();
        char[] chs = new char[tchs.length*2+1];
        int idx = 0;
        for(int i=0;i<chs.length;i++){
            chs[i] = (i & 1) == 1 ? tchs[idx++] : '#';
        }
        int[] pArr = new int[chs.length];
        int R = -1;
        int C = -1;
        int max = Integer.MIN_VALUE;
        int index = -1;
        for(int i=0;i<chs.length;i++){
            pArr[i] = i < R ? Math.min(pArr[2*C-i],R-i) : 1;
            while(i-pArr[i] >= 0 && i+pArr[i]<chs.length){
                if(chs[i+pArr[i]] == chs[i-pArr[i]]){
                    pArr[i]++;
                }else{
                    break;
                }
            }
            if(i+pArr[i] > R){
                R = i + pArr[i];
                C = i;
            }
            if(pArr[i] >= max){
                max = pArr[i];
                index = i;
            }
        }
        String ans = new String();
        for(int i=index-max+1;i<=index+max-1;i++){
            if(chs[i] != '#') ans += chs[i];
        }
        return ans;
    }
}

当我们的 i 还在最右侧的内部时,我们只需要从

Math.min(pArr[2*C-i],R-i)

中的最小值开始遍历就行了,很好理解,此处不展开。就是上面的3种情况。否则只能从1重新开始。遍历结束后,如果现有的范围已经超过原R,扩展R同时改变C为当前的C。

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

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

相关文章

顺势交易中,用什么方法识别趋势的开始与结束?

在交易过程中&#xff0c;大家都知道顺势交易的重要性&#xff0c;但如何对趋势的开始和结束进行量化判断呢&#xff1f; 趋势交易需要一个正确的出发点和思想方向。也就是说&#xff0c;趋势交易需要关注什么呢&#xff1f;有哪些相关的技术手段可以利用呢&#xff1f; 首先&a…

springboot使用异步多线程

shigen坚持更新文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 个人IP&#xff1a;shigen 在shigen之前的很多文章中&#xff0c;提到了线程池&#xff1a; 高性能API设计…

一 windso10 笔记本刷linux cent os7.9系统

1:准备材料 16G以上U盘, 笔记本一台 镜像选了阿里云镜像:centos-7-isos-x86_64安装包下载_开源镜像站-阿里云 软件:链接&#xff1a;https://pan.baidu.com/s/13WDp2bBU1Pdx4gRDfmBetg 提取码&#xff1a;09s3 2:把镜像写入U盘,本人已经写入好了,选择镜像,点开始就是,确定等…

javascript正则深入

文章目录 一、前言二、高级`API`2.1、模式匹配的用法`(x)`2.2、非捕获括号的模式匹配`(?:x)`2.3、先行断言`x(?=y)`2.4、后行断言`(?<=y)x`2.5、正向否定查找`x(?!y)`2.6、反向否定查找`(?<!y)x`2.7、字符集合和反向字符集合的用法 `[xyz] / [^xyz]`2.8、词边界和非…

开关电源安规测试标准与测试要求

安规测试是对开关电源进行电气性能、安全性能等检测&#xff0c;确保开关电源符合规定并且安全可靠&#xff0c;为开关电源的质量把关。那么开关电源安规测试有哪些测试要求和标准呢&#xff1f; 开关电源安规测试要求 一、测试前 1. 首先&#xff0c;要检查测试环境&#xff0…

电子商务平台中商品数据采集|API数据接口采集商品的高效性体现

电子商务平台由一个个产品所构成,通过对产品的分析可以有效发掘用户需求,优化店铺产品结构,提升客户粘性、客单价、销售利润等。 一、产品行业数据采集API接口 采集产品行业数据的核心目的是了解该产品的市场需求变化情况,常用到的数据采集指标包括产品搜索指数和产品交易指数两…

项目建设计划书-word

【项目建设计划书-word】 项目描述&#xff08;项目目标&#xff0c;客户需求情况&#xff0c;项目交付清单&#xff0c;验收标准和交付期限&#xff0c;服务及约束&#xff09;项目组织&#xff08;项目组人员架构&#xff0c;职责分工&#xff0c;人员投入安排及时间点安排&…

Python爬虫——Scrapy-1

目录 简介 安装 基本使用 1. 创建爬虫的项目 2. 创建爬虫文件 3. 运行爬虫代码 scrapy项目组成 scrapy工作原理 ​编辑 58同城 scrapy架构组成 汽车之家 总结 简介 Scrapy 是一个基于 Python 的开源网络爬虫框架&#xff0c;它可以帮助开发者快速、高效地构…

MySQL和语言的连接

1.下载开发包 有了之前的基础&#xff0c;就可以使用语言来使用数据库了&#xff0c;实际上原生命令行操作数据库的场景比较少&#xff0c;语言级别的库和包才更加常用&#xff0c;接下我来带您学习如何使用 C/C 访问 MySQL 客户端。 然后创建一个可以远程登录的用户&#xf…

Transformer中的FlashAttention

FlashAttention是一种用于Transformer模型的近似注意力机制&#xff0c;旨在减少注意力计算和内存需求。引入FlashAttention是因为传统Transformer模型中的自注意力机制在处理长序列时存在时间和存储复杂度上的挑战&#xff0c;需要大量的计算资源和内存来处理更长的上下文背景…

【数据结构】二、线性表:6.顺序表和链表的对比不同(从数据结构三要素讨论:逻辑结构、物理结构(存储结构)、数据运算(基本操作))

文章目录 6.对比&#xff1a;顺序表&链表6.1逻辑结构6.2物理结构&#xff08;存储结构&#xff09;6.2.1顺序表6.2.2链表 6.3数据运算&#xff08;基本操作&#xff09;6.3.1初始化6.3.2销毁表6.3.3插入、删除6.3.4查找 6.对比&#xff1a;顺序表&链表 6.1逻辑结构 顺…

禁闭室的乐趣

解法&#xff1a; 暴力 #include<iostream> #include<vector> #include<algorithm> using namespace std; #define endl \n bool cmp(string& a, string& b) {if (a.size() ! b.size())return a.size() > b.size(); } int main() {ios::sync_wi…

【李沐精读系列】GPT、GPT-2和GPT-3论文精读

论文&#xff1a; GPT&#xff1a;Improving Language Understanding by Generative Pre-Training GTP-2&#xff1a;Language Models are Unsupervised Multitask Learners GPT-3&#xff1a;Language Models are Few-Shot Learners 参考&#xff1a;GPT、GPT-2、GPT-3论文精读…

CentOS7 利用remi yum源安装php8.1

目录 前言remi yum源remi yum源 支持的操作系统remi yum源 支持的php版本 安装epel源安装remi源安装 php8.1查看php版本查看php-fpm服务启动php-fpm服务查看php-fpm服务运行状态查看php-fpm服务占用的端口查看 php8.1 相关的应用 前言 CentOS Linux release 7.9.2009 (Core) …

基于java springboot网上蛋糕商城项目设计和实现

基于java springboot网上蛋糕商城项目设计和实现 博主介绍&#xff1a;多年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐留言 文末获取源…

酷克数据荣获粤港澳大湾区金融创新成果“特优解决方案”奖

近日&#xff0c;“第二届金融创新优秀应用案例与解决方案技术成果评定”正式揭晓&#xff0c;酷克数据提交的“HashData云原生金融信创数据仓库”入选特优解决方案&#xff0c;成为数据治理与数据平台类目中唯一的厂商方案。 本次评定历时5个多月&#xff0c;共257个项目参与申…

了解开源可视化表单的主要优势

为什么可视化表单深受大家喜爱&#xff1f;这就需要了解开源可视化表单的优势和特点了。在流程化办公深入人心的今天&#xff0c;提高办公协作效率早已成为大家的发展目标&#xff0c;低代码技术平台、开源可视化表单是提升办公协作效率的得力助手&#xff0c;一起来看看它的优…

项目管理工具及模板(甘特图、OKR周报、任务管理、头脑风暴等)

项目管理常用模板大全&#xff1a; 1. 项目组OKR周报 2. 项目组传统周报工作法 3. 项目甘特图 4. 团队名单 5. 招聘跟进表 6. 出勤统计 7. 年度工作日历 8. 项目工作年计划 9. 版本排期 10. 项目组任务管理 11. 项目规划模板 12. 产品分析报告 13. 头脑风暴 信息化项目建设全套…

什么是TikTok账号权重?打破TikTok0播放的方法

许多TikTok账号运营者都会遇到一个难题&#xff0c;那就是视频要么播放量很低&#xff0c;要么0播放&#xff01;不管内容做的多好&#xff0c;最好都是竹篮打水一场空&#xff01;其实你可能忽略了一个问题&#xff0c;那就是账号权重。下面好好跟大家讲讲这个东西&#xff01…

5V转3.3V/2.5V/1.2V芯片PW2053,专为锂电池设计,低功耗外围简单

在现代电子设备领域&#xff0c;高效、稳定的电源管理至关重要。为了满足这一需求&#xff0c;我们向您隆重推荐PW2053高效同步降压调节器。这款调节器以其出色的性能和广泛的应用领域&#xff0c;赢得了市场的广泛认可。 一、产品描述 PW2053是一款采用恒定频率、电流模式架…