(LeetCode-Hot100)5. 最长回文子串

问题简介

🔗 LeetCode 5. 最长回文子串

题目描述

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

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


示例说明

示例 1:

输入:s = "babad"

输出:"bab"

解释:"aba" 同样是符合题意的答案。

示例 2:

输入:s = "cbbd"

输出:"bb"

示例 3:

输入:s = "a"

输出:"a"

示例 4:

输入:s = "ac"

输出:"a""c"


解题思路

📌 回文串具有对称性。我们可以从中心向两边扩展(中心扩散法),也可以使用动态规划记录子串是否为回文。

✅ 方法一:中心扩散法(推荐)
  • 枚举每一个可能的回文中心(包括奇数长度和偶数长度)。
  • 对于每个中心,向左右扩展,直到字符不匹配。
  • 记录过程中最长的回文子串。

💡 关键点:

  • 奇数长度回文:中心是一个字符(如 "aba",中心是 'b')。
  • 偶数长度回文:中心在两个字符之间(如 "abba",中心在两个 'b' 之间)。

时间复杂度较低,代码简洁。

✅ 方法二:动态规划
  • 定义 dp[i][j] 表示子串 s[i..j] 是否为回文。
  • 状态转移:
    • 如果 s[i] == s[j](j - i < 2 || dp[i+1][j-1]),则 dp[i][j] = true
  • 遍历所有子串,更新最长回文。

空间复杂度较高,但逻辑清晰。

❌ 方法三:暴力枚举(不推荐)
  • 枚举所有子串,判断是否为回文。
  • 时间复杂度 O(n³),会超时。

代码实现

java:Java 复制代码
class Solution {
    public String longestPalindrome(String s) {
        if (s == null || s.length() < 1) return "";
        int start = 0, end = 0;
        for (int i = 0; i < s.length(); i++) {
            int len1 = expandAroundCenter(s, i, i);         // 奇数长度
            int len2 = expandAroundCenter(s, i, i + 1);     // 偶数长度
            int len = Math.max(len1, len2);
            if (len > end - start) {
                start = i - (len - 1) / 2;
                end = i + len / 2;
            }
        }
        return s.substring(start, end + 1);
    }

    private int expandAroundCenter(String s, int left, int right) {
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
            left--;
            right++;
        }
        return right - left - 1;
    }
}
go:Go 复制代码
func longestPalindrome(s string) string {
    if len(s) == 0 {
        return ""
    }
    start, end := 0, 0
    for i := 0; i < len(s); i++ {
        len1 := expandAroundCenter(s, i, i)
        len2 := expandAroundCenter(s, i, i+1)
        length := max(len1, len2)
        if length > end-start {
            start = i - (length-1)/2
            end = i + length/2
        }
    }
    return s[start : end+1]
}

func expandAroundCenter(s string, left, right int) int {
    for left >= 0 && right < len(s) && s[left] == s[right] {
        left--
        right++
    }
    return right - left - 1
}

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

💡 上述代码均采用中心扩散法,简洁高效。


示例演示

s = "babad" 为例:

中心位置 类型 扩展过程 回文长度 当前最长
i=0 "b" 1 "b"
i=0 "ba" 0 "b"
i=1 "a" → "bab" 3 "bab"
i=1 "ab" 0 "bab"
i=2 "b" → "aba" 3 "bab"(或 "aba"
... ... ... ... ...

最终返回 "bab""aba",均合法。


答案有效性证明

正确性:

  • 所有可能的回文中心都被枚举(共 2n - 1 个:n 个单字符 + n-1 个双字符间隙)。
  • 每次扩展都保证了回文性质(左右字符相等才继续)。
  • 记录了最大长度对应的起止位置,确保结果正确。

边界处理:

  • 空字符串、单字符、无回文(如 "abc")均能正确返回。

复杂度分析

方法 时间复杂度 空间复杂度
中心扩散法 O(n²) O(1)
动态规划 O(n²) O(n²)
暴力枚举 O(n³) O(1)

📌 中心扩散法最优:常数空间,且实际运行快(内层循环通常提前终止)。


问题总结

核心思想:利用回文的对称性,从中心向外扩展。

技巧

  • 同时处理奇偶长度回文。
  • 通过 startend 记录位置,避免频繁字符串拼接。

适用场景:适用于所有需要查找回文子串的问题(如回文子串个数、最长回文子序列等变种)。

💡 延伸思考

  • 若需找所有回文子串,可修改中心扩散法收集结果。
  • 更优解法(如 Manacher 算法)可在 O(n) 时间解决,但实现复杂,面试中较少要求。

github地址: https://github.com/swf2020/LeetCode-Hot100-Solutions

相关推荐
小灵不想卷1 小时前
LangChain4j 多模态
java·langchain4j
tankeven1 小时前
HJ86 求最大连续bit数
c++·算法
wuqingshun3141591 小时前
String、StringBuffer、StringBuilder的应用场景
java·开发语言·jvm
ValhallaCoder1 小时前
hot100-回溯II
数据结构·python·算法·回溯
日月云棠2 小时前
JDK 17 特性详解
java
追随者永远是胜利者2 小时前
(LeetCode-Hot100)19. 删除链表的倒数第 N 个结点
java·算法·leetcode·链表·go
小李独爱秋2 小时前
模拟面试:什么是容器技术,Docker是什么?
运维·docker·容器·面试·职场和发展
树码小子2 小时前
Mybatis(14)Mybatis-Plus入门 & 简单使用
java·mybatis-plus
人道领域2 小时前
Maven配置加载:动态替换的艺术
java·数据库·后端