文章目录

  • 5. 最长回文子串
    • 描述
    • 示例 1
    • 示例 2
    • 示例 3
    • 示例 4
    • 提示
    • 解题思路
      • 方法一:中心扩展法(推荐)
      • 方法二:动态规划
      • 方法三:Manacher算法
      • 方法四:暴力解法
    • 代码实现
    • 复杂度分析
    • 测试用例
    • 完整题解代码

5. 最长回文子串

描述

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

示例 1

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

示例 2

输入:s = “cbbd”
输出:“bb”

示例 3

输入:s = “a”
输出:“a”

示例 4

输入:s = “ac”
输出:“a”

提示

  • 1 <= s.length <= 1000
  • s 仅由数字和英文字母组成

解题思路

方法一:中心扩展法(推荐)

核心思想

  • 以每个字符为中心,向两边扩展寻找回文
  • 考虑奇数长度和偶数长度的回文

算法步骤

  1. 遍历字符串的每个位置
  2. 以当前位置为中心,向两边扩展
  3. 分别处理奇数长度和偶数长度的回文
  4. 记录最长回文的起始位置和长度

时间复杂度:O(n²)
空间复杂度:O(1)

方法二:动态规划

核心思想

  • 使用dp[i][j]表示s[i:j+1]是否为回文
  • 状态转移:dp[i][j] = (s[i] == s[j]) && dp[i+1][j-1]

算法步骤

  1. 创建二维dp数组
  2. 初始化长度为1和2的回文
  3. 按长度递增填充dp数组
  4. 记录最长回文的位置

时间复杂度:O(n²)
空间复杂度:O(n²)

方法三:Manacher算法

核心思想

  • 使用马拉车算法在线性时间内找到最长回文
  • 利用回文的对称性质优化计算

算法步骤

  1. 预处理字符串,插入分隔符
  2. 维护回文半径数组
  3. 利用对称性质优化扩展
  4. 找到最大回文半径

时间复杂度:O(n)
空间复杂度:O(n)

方法四:暴力解法

核心思想

  • 枚举所有可能的子串
  • 检查每个子串是否为回文

算法步骤

  1. 双重循环枚举所有子串
  2. 检查每个子串是否为回文
  3. 记录最长回文

时间复杂度:O(n³)
空间复杂度:O(1)

代码实现

// 方法一:中心扩展法
func longestPalindrome1(s string) string {if len(s) < 2 {return s}start, maxLen := 0, 1for i := 0; i < len(s); i++ {// 奇数长度回文len1 := expandAroundCenter(s, i, i)// 偶数长度回文len2 := expandAroundCenter(s, i, i+1)maxLenCur := max(len1, len2)if maxLenCur > maxLen {start = i - (maxLenCur-1)/2maxLen = maxLenCur}}return s[start : start+maxLen]
}func expandAroundCenter(s string, left, right int) int {for left >= 0 && right < len(s) && s[left] == s[right] {left--right++}return right - left - 1
}

复杂度分析

  • 时间复杂度:O(n²),其中n是字符串长度
  • 空间复杂度:O(1),只使用常数个变量

测试用例

func main() {// 测试用例1s1 := "babad"fmt.Printf("测试用例1: s=%s, 结果=%s\n", s1, longestPalindrome1(s1))// 测试用例2s2 := "cbbd"fmt.Printf("测试用例2: s=%s, 结果=%s\n", s2, longestPalindrome1(s2))// 测试用例3s3 := "a"fmt.Printf("测试用例3: s=%s, 结果=%s\n", s3, longestPalindrome1(s3))
}

完整题解代码

package mainimport "fmt"// 方法一:中心扩展法(推荐)
// 时间复杂度:O(n²),空间复杂度:O(1)
func longestPalindrome1(s string) string {if len(s) < 2 {return s}start, maxLen := 0, 1for i := 0; i < len(s); i++ {// 奇数长度回文len1 := expandAroundCenter(s, i, i)// 偶数长度回文len2 := expandAroundCenter(s, i, i+1)maxLenCur := max(len1, len2)if maxLenCur > maxLen {start = i - (maxLenCur-1)/2maxLen = maxLenCur}}return s[start : start+maxLen]
}func expandAroundCenter(s string, left, right int) int {for left >= 0 && right < len(s) && s[left] == s[right] {left--right++}return right - left - 1
}// 方法二:动态规划
// 时间复杂度:O(n²),空间复杂度:O(n²)
func longestPalindrome2(s string) string {if len(s) < 2 {return s}n := len(s)dp := make([][]bool, n)for i := range dp {dp[i] = make([]bool, n)}start, maxLen := 0, 1// 初始化长度为1的回文for i := 0; i < n; i++ {dp[i][i] = true}// 初始化长度为2的回文for i := 0; i < n-1; i++ {if s[i] == s[i+1] {dp[i][i+1] = truestart = imaxLen = 2}}// 按长度递增填充dp数组for length := 3; length <= n; length++ {for i := 0; i <= n-length; i++ {j := i + length - 1if s[i] == s[j] && dp[i+1][j-1] {dp[i][j] = trueif length > maxLen {start = imaxLen = length}}}}return s[start : start+maxLen]
}// 方法三:Manacher算法
// 时间复杂度:O(n),空间复杂度:O(n)
func longestPalindrome3(s string) string {if len(s) < 2 {return s}// 预处理字符串,插入分隔符t := "#"for _, char := range s {t += string(char) + "#"}n := len(t)p := make([]int, n)center, right := 0, 0// 计算回文半径for i := 0; i < n; i++ {if i < right {mirror := 2*center - ip[i] = min(right-i, p[mirror])}// 尝试扩展left := i - (p[i] + 1)right_bound := i + (p[i] + 1)for left >= 0 && right_bound < n && t[left] == t[right_bound] {p[i]++left--right_bound++}// 更新中心点和右边界if i+p[i] > right {center = iright = i + p[i]}}// 找到最大回文半径maxRadius := 0maxCenter := 0for i := 0; i < n; i++ {if p[i] > maxRadius {maxRadius = p[i]maxCenter = i}}// 转换回原字符串的索引start := (maxCenter - maxRadius) / 2end := (maxCenter + maxRadius) / 2return s[start:end]
}// 方法四:暴力解法
// 时间复杂度:O(n³),空间复杂度:O(1)
func longestPalindrome4(s string) string {if len(s) < 2 {return s}start, maxLen := 0, 1for i := 0; i < len(s); i++ {for j := i + 1; j < len(s); j++ {if isPalindrome(s, i, j) && j-i+1 > maxLen {start = imaxLen = j - i + 1}}}return s[start : start+maxLen]
}func isPalindrome(s string, left, right int) bool {for left < right {if s[left] != s[right] {return false}left++right--}return true
}// 方法五:优化的中心扩展法
// 时间复杂度:O(n²),空间复杂度:O(1)
func longestPalindrome5(s string) string {if len(s) < 2 {return s}start, maxLen := 0, 1for i := 0; i < len(s); i++ {// 奇数长度回文len1 := expandAroundCenterOptimized(s, i, i)// 偶数长度回文len2 := expandAroundCenterOptimized(s, i, i+1)maxLenCur := max(len1, len2)if maxLenCur > maxLen {start = i - (maxLenCur-1)/2maxLen = maxLenCur}}return s[start : start+maxLen]
}func expandAroundCenterOptimized(s string, left, right int) int {for left >= 0 && right < len(s) && s[left] == s[right] {left--right++}return right - left - 1
}// 方法六:使用字符串哈希(Rabin-Karp思想)
// 时间复杂度:O(n²),空间复杂度:O(1)
func longestPalindrome6(s string) string {if len(s) < 2 {return s}start, maxLen := 0, 1for i := 0; i < len(s); i++ {// 奇数长度回文len1 := expandAroundCenter(s, i, i)// 偶数长度回文len2 := expandAroundCenter(s, i, i+1)maxLenCur := max(len1, len2)if maxLenCur > maxLen {start = i - (maxLenCur-1)/2maxLen = maxLenCur}}return s[start : start+maxLen]
}// 辅助函数
func max(a, b int) int {if a > b {return a}return b
}func min(a, b int) int {if a < b {return a}return b
}func main() {fmt.Println("=== 5. 最长回文子串 ===")// 测试用例1s1 := "babad"fmt.Printf("测试用例1: s=%s\n", s1)fmt.Printf("方法一结果: %s\n", longestPalindrome1(s1))fmt.Printf("方法二结果: %s\n", longestPalindrome2(s1))fmt.Printf("方法三结果: %s\n", longestPalindrome3(s1))fmt.Printf("方法四结果: %s\n", longestPalindrome4(s1))fmt.Printf("方法五结果: %s\n", longestPalindrome5(s1))fmt.Printf("方法六结果: %s\n", longestPalindrome6(s1))fmt.Println()// 测试用例2s2 := "cbbd"fmt.Printf("测试用例2: s=%s\n", s2)fmt.Printf("方法一结果: %s\n", longestPalindrome1(s2))fmt.Printf("方法二结果: %s\n", longestPalindrome2(s2))fmt.Printf("方法三结果: %s\n", longestPalindrome3(s2))fmt.Printf("方法四结果: %s\n", longestPalindrome4(s2))fmt.Printf("方法五结果: %s\n", longestPalindrome5(s2))fmt.Printf("方法六结果: %s\n", longestPalindrome6(s2))fmt.Println()// 测试用例3s3 := "a"fmt.Printf("测试用例3: s=%s\n", s3)fmt.Printf("方法一结果: %s\n", longestPalindrome1(s3))fmt.Printf("方法二结果: %s\n", longestPalindrome2(s3))fmt.Printf("方法三结果: %s\n", longestPalindrome3(s3))fmt.Printf("方法四结果: %s\n", longestPalindrome4(s3))fmt.Printf("方法五结果: %s\n", longestPalindrome5(s3))fmt.Printf("方法六结果: %s\n", longestPalindrome6(s3))fmt.Println()// 测试用例4s4 := "ac"fmt.Printf("测试用例4: s=%s\n", s4)fmt.Printf("方法一结果: %s\n", longestPalindrome1(s4))fmt.Printf("方法二结果: %s\n", longestPalindrome2(s4))fmt.Printf("方法三结果: %s\n", longestPalindrome3(s4))fmt.Printf("方法四结果: %s\n", longestPalindrome4(s4))fmt.Printf("方法五结果: %s\n", longestPalindrome5(s4))fmt.Printf("方法六结果: %s\n", longestPalindrome6(s4))fmt.Println()// 额外测试用例s5 := "racecar"fmt.Printf("额外测试: s=%s\n", s5)fmt.Printf("方法一结果: %s\n", longestPalindrome1(s5))fmt.Printf("方法二结果: %s\n", longestPalindrome2(s5))fmt.Printf("方法三结果: %s\n", longestPalindrome3(s5))fmt.Printf("方法四结果: %s\n", longestPalindrome4(s5))fmt.Printf("方法五结果: %s\n", longestPalindrome5(s5))fmt.Printf("方法六结果: %s\n", longestPalindrome6(s5))fmt.Println()// 边界测试用例s6 := ""fmt.Printf("边界测试: s=%s\n", s6)fmt.Printf("方法一结果: %s\n", longestPalindrome1(s6))fmt.Printf("方法二结果: %s\n", longestPalindrome2(s6))fmt.Printf("方法三结果: %s\n", longestPalindrome3(s6))fmt.Printf("方法四结果: %s\n", longestPalindrome4(s6))fmt.Printf("方法五结果: %s\n", longestPalindrome5(s6))fmt.Printf("方法六结果: %s\n", longestPalindrome6(s6))fmt.Println()// 复杂测试用例s7 := "abbaabba"fmt.Printf("复杂测试: s=%s\n", s7)fmt.Printf("方法一结果: %s\n", longestPalindrome1(s7))fmt.Printf("方法二结果: %s\n", longestPalindrome2(s7))fmt.Printf("方法三结果: %s\n", longestPalindrome3(s7))fmt.Printf("方法四结果: %s\n", longestPalindrome4(s7))fmt.Printf("方法五结果: %s\n", longestPalindrome5(s7))fmt.Printf("方法六结果: %s\n", longestPalindrome6(s7))fmt.Println()// 全相同字符测试用例s8 := "aaaa"fmt.Printf("全相同字符测试: s=%s\n", s8)fmt.Printf("方法一结果: %s\n", longestPalindrome1(s8))fmt.Printf("方法二结果: %s\n", longestPalindrome2(s8))fmt.Printf("方法三结果: %s\n", longestPalindrome3(s8))fmt.Printf("方法四结果: %s\n", longestPalindrome4(s8))fmt.Printf("方法五结果: %s\n", longestPalindrome5(s8))fmt.Printf("方法六结果: %s\n", longestPalindrome6(s8))fmt.Println()// 数字字符串测试用例s9 := "12321"fmt.Printf("数字字符串测试: s=%s\n", s9)fmt.Printf("方法一结果: %s\n", longestPalindrome1(s9))fmt.Printf("方法二结果: %s\n", longestPalindrome2(s9))fmt.Printf("方法三结果: %s\n", longestPalindrome3(s9))fmt.Printf("方法四结果: %s\n", longestPalindrome4(s9))fmt.Printf("方法五结果: %s\n", longestPalindrome5(s9))fmt.Printf("方法六结果: %s\n", longestPalindrome6(s9))fmt.Println()// 混合字符串测试用例s10 := "a1b2c3c2b1a"fmt.Printf("混合字符串测试: s=%s\n", s10)fmt.Printf("方法一结果: %s\n", longestPalindrome1(s10))fmt.Printf("方法二结果: %s\n", longestPalindrome2(s10))fmt.Printf("方法三结果: %s\n", longestPalindrome3(s10))fmt.Printf("方法四结果: %s\n", longestPalindrome4(s10))fmt.Printf("方法五结果: %s\n", longestPalindrome5(s10))fmt.Printf("方法六结果: %s\n", longestPalindrome6(s10))
}

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

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

相关文章

【图像处理基石】如何对遥感图像进行实例分割?

遥感图像实例分割是指在遥感影像中&#xff0c;不仅要识别出不同类别的目标&#xff08;如建筑物、车辆、道路等&#xff09;&#xff0c;还要区分同一类别中的不同个体&#xff08;如建筑物1、建筑物2&#xff09;&#xff0c;并为每个实例生成精确的像素级掩码。 一、遥感图…

电子电气架构 --- 软件bug的管理模式

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活,除了生存温饱问题之外,没有什么过多的欲望,表面看起来很高冷,内心热情,如果你身…

【每日一错】Oracle 19c CDB中如何启动一个PDB

文章目录题目扩展学习CDB与PDB的概念CDB&#xff0c;PDB结构优势总结题目 扩展学习 CDB与PDB的概念 在Oracle 12c及以上版本&#xff0c;Oracle引入了多租户架构&#xff0c;这种架构让数据库的管理和资源使用更加高效。它由两种主要组成部分组成&#xff1a; CDB&#xff0…

Android studio自带的Android模拟器都是x86架构的吗,需要把arm架构的app翻译成x86指令?

Android studio自带的Android模拟器都是x86架构的吗&#xff0c;需要把arm架构的app翻译成x86指令&#xff1f; deepseek回答&#xff1a; Android Studio 自带的官方模拟器&#xff08;Android Emulator&#xff09;主要提供基于 x86 架构的系统镜像。当运行 ARM 架构的应用…

Deep Learning_ Foundations and Concepts-Springer (2024)【拜读】20章3节

Diffusion Models 扩散模型 我们已经了解到&#xff0c;构建强大的生成模型的一种有效方法是&#xff1a;先引入一个关于潜在变量z的分布p(z)&#xff0c;然后使用深度神经网络将z变换到数据空间x。由于神经网络具有通用性&#xff0c;能够将简单固定的分布转化为关于x的高度灵…

Arduino与STM32:初学者该如何选择?

在电子爱好者和初学者的世界里&#xff0c;Arduino和STM32是两个经常被提及的名字。它们各自具有独特的优势和特点&#xff0c;适合不同类型的项目和需求。对于初学者来说&#xff0c;选择Arduino还是STM32&#xff0c;往往取决于个人的学习目标、项目需求以及预算。本文将详细…

创建型设计模式-工厂方法模式和抽象工厂方法模式

1、工厂方法模式 创建型设计模式之一 UML类图2、抽象工厂模式 也是创建型设计模式之一。虽然抽象工厂方法模式的类繁多&#xff0c;但是&#xff0c;主要分为4类。 AbstractFactory&#xff1a;抽象工厂角色&#xff0c;它声明了一组用于创建一种产品的方法&#xff0c;每一个方…

Hyperchain安全与隐私机制详解

一、核心安全机制1. 共识算法安全RBFT共识算法&#xff1a;改进型PBFT&#xff1a;基于PBFT算法优化&#xff0c;增加动态节点管理、失效数据恢复机制&#xff0c;提升系统容错性与可用性。性能指标&#xff1a;吞吐量稳定达3000-10000 TPS&#xff0c;交易执行时间控制在300ms…

Oracle优化学习十六

反连接反连接&#xff08;Anti Join&#xff09;是一种特殊的连接类型&#xff0c;与内连接和外连接不同&#xff0c;Oracle数据库里并没有相关的 关键字可以在SQL文本中专门表示反连接&#xff0c;所以这里把它单独拿出来说明。为了方便说明反连接的含义&#xff0c;我们用“t…

梳理一些 Docker 常用命令

以下是一些 Docker 常用命令&#xff0c;适用于日常开发、调试、部署等场景&#xff0c;分为几个常用类别&#xff1a;&#x1f4e6; 一、镜像&#xff08;Image&#xff09;相关命令命令说明docker images查看本地所有镜像docker pull <image>拉取镜像&#xff08;如 do…

C#_ArrayList动态数组

目录 ArrayList的特点 ArrayList 与普通数组的区别 使用示例&#xff1a; 普通数组 动态数组 主要方法和属性 属性&#xff1a; Count 获取动态数组的数据个数 读取某个位置的数据 // 索引 方法&#xff1a; Add 向集合末尾添加元素 Insert 在指定位置插入元…

Agent领域,近年来的前沿研究方向:多智能体协作、认知启发架构、伦理安全、边缘计算集成

Agent领域,近年来的前沿研究方向:多智能体协作、认知启发架构、伦理安全、边缘计算集成 在Agent领域,近年来的前沿研究方向主要集中在多智能体协作、认知启发架构、伦理安全、边缘计算集成以及生成式AI融合等方面。 一、多智能体协作与多模态任务 多智能体系统在复杂环境…

【安卓笔记】OOM与内存优化

0. 环境&#xff1a; 电脑&#xff1a;Windows10 Android Studio: 2024.3.2 编程语言: Java Gradle version&#xff1a;8.11.1 Compile Sdk Version&#xff1a;35 Java 版本&#xff1a;Java11 1.什么是OOM OOM即 OutOfMemoryError 内存溢出错误。常见于一些 资源型对…

持续集成CI与自动化测试

Python接口自动化测试零基础入门到精通&#xff08;2025最新版&#xff09;

Spring 策略模式实现

Spring 策略模式实现&#xff1a;工厂方法与自动注入详解 1. 背景介绍 在复杂的业务系统中,我们常常需要根据不同的场景选择不同的处理策略。本文将详细介绍在 Spring 框架中实现策略模式的两种主要方法。 2. 方案一: 手动注册工厂模式 2.1 定义工厂类 Component public class …

机器学习——线性回归(LinearRegression)

Python 线性回归详解&#xff1a;从原理到实战线性回归&#xff08;Linear Regression&#xff09;是机器学习中最基础也是最重要的算法之一&#xff0c;广泛应用于预测分析领域&#xff0c;例如房价预测、销售额预测等。本文将带你从理论出发&#xff0c;用 Python 手把手实现…

H.264视频的RTP有效载荷格式(翻译自:RFC6184 第5节 RTP有效载荷格式)

RTP协议格式 RFC地址&#xff1a;https://datatracker.ietf.org/doc/html/rfc6184 RTP报头的格式在RFC3550中指定 0 1 2 30 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1------------------------…

秒级构建消息驱动架构:描述事件流程,生成 Spring Cloud Stream+RabbitMQ 代码

在消息驱动架构开发中&#xff0c;Spring Cloud Stream 与 RabbitMQ 的整合往往需要手动配置绑定器、定义消息通道、编写消费逻辑&#xff0c;流程繁琐且易出错。而飞算JavaAI 作为高效的 IDE 插件&#xff0c;能让开发者通过自然语言描述事件流程&#xff0c;自动生成可运行的…

从零搭建3D激光slam框架-基于mid360雷达节点实现

目录 MID360雷达介绍 雷达SDK编译与测试 雷达驱动的修改、编译与测试 去ros的编译方式 livox_ros_driver2的代码框架介绍 livox_ros_driver2编译 雷达IP配置文件介绍 常见问题介绍 优化改进 MID360雷达介绍 1 硬件介绍&#xff1a; livox-mid360是大疆的一款非重复扫描…

【Spring】日志级别的分类和使用

文章目录介绍日志级别的分类日志级别的顺序日志级别的使用介绍 日志级别代表着日志信息对应问题的严重性&#xff0c;为了更快的筛选符合目标的日志信息 试想一下这样的场景&#xff0c;假设你是一家 2 万人公司的老板&#xff0c;如果每个员工的日常工作和琐碎的信息都要反馈…