LeetCode Hot100(57/100)——5. 最长回文子串

文章目录

题目链接
LeetCode - Longest Palindromic Substring


一、题目描述

给定一个字符串 s,请你找出其中的 最长回文子串

回文子串指的是正着读和反着读都相同的字符串片段。

示例:

复制代码
输入: s = "babad"
输出: "bab"
解释: "aba" 同样是有效答案

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

二、问题分析

回文的特征是 对称,因此求解过程的核心就是如何找到字符串中的对称中心并向两边扩展。

下图从思维层面展示了几种常见算法思路:
最长回文子串
暴力枚举
检查所有子串是否回文
时间复杂度 O(n³)
动态规划
状态定义 dp[i][j]
转移方程依据 s[i]==s[j]
时间复杂度 O(n²)
中心扩展
每个字符为中心
向两边扩展计算长度
时间复杂度 O(n²)
Manacher算法
利用镜像特性
线性时间求解 O(n)


三、解法一:暴力枚举

原理

  • 枚举所有可能的子串。
  • 对每个子串判断是否为回文。
  • 记录最大长度的回文子串。

流程图



输入字符串 s
枚举所有子串 s[i:j]
判断是否为回文
是回文?
更新最长长度与起止下标
继续下一子串
输出最长回文子串

时间复杂度

  • 枚举子串次数约为 O(n²)
  • 判定回文 O(n)
  • 总时间复杂度:O(n³)
  • 空间复杂度:O(1)

属于理论上最简单但效率最低的方式,适合初学者理解。


四、解法二:动态规划(DP)

原理

使用二维数组 dp[i][j] 表示 s[i...j] 是否为回文字符串

  • s[i] == s[j] 时,若 dp[i+1][j-1] 为真,则 dp[i][j] 也为真。
  • 边界情况:单个字符必为回文 (dp[i][i] = true)

状态转移方程:

d p [ i ] [ j ] = ( s [ i ] = = s [ j ] ) & & ( j − i < 3 ∥ d p [ i + 1 ] [ j − 1 ] ) dp[i][j] = (s[i] == s[j]) \ \&\& \ (j - i < 3 \ \| \ dp[i+1][j-1]) dp[i][j]=(s[i]==s[j]) && (j−i<3 ∥ dp[i+1][j−1])

伪逻辑时序

状态表 dp[i][j] 内层循环 j ← i 到 n-1 外层循环 i ← n-1 到 0 状态表 dp[i][j] 内层循环 j ← i 到 n-1 外层循环 i ← n-1 到 0 逐步扩大区间 [i...j] 判断 s[i]==s[j] 且区间内部为回文 若成立则更新最大长度

时间复杂度

  • 状态数量 O(n²)
  • 每次转移 O(1)
  • 总复杂度:O(n²)
  • 空间复杂度:O(n²)

Java 实现

java 复制代码
public class Solution {
    public String longestPalindrome(String s) {
        int n = s.length();
        boolean[][] dp = new boolean[n][n];
        String res = "";
        for (int i = n - 1; i >= 0; i--) {
            for (int j = i; j < n; j++) {
                if (s.charAt(i) == s.charAt(j) && (j - i < 3 || dp[i + 1][j - 1])) {
                    dp[i][j] = true;
                }
                if (dp[i][j] && (j - i + 1) > res.length()) {
                    res = s.substring(i, j + 1);
                }
            }
        }
        return res;
    }
}

五、解法三:中心扩展法

原理

回文子串的"中心"可以是:

  • 一个字符(奇数长度回文)
  • 两个相邻字符(偶数长度回文)

从每个中心向两侧扩展,直到不再匹配。

示意图

中心 i/i+1
左边扩展 →
← 右边扩展
判断 s[left]==s[right]
更新最长回文长度

时间复杂度

  • 中心共有 2n-1 个。
  • 每次扩展最多 O(n)
  • 总复杂度:O(n²)
  • 空间复杂度:O(1)

Java 实现

java 复制代码
public class Solution {
    public String longestPalindrome(String s) {
        if (s == null || s.length() < 2) return s;
        int start = 0, maxLen = 1;
        for (int i = 0; i < s.length(); i++) {
            int len1 = expandFromCenter(s, i, i);
            int len2 = expandFromCenter(s, i, i + 1);
            int len = Math.max(len1, len2);
            if (len > maxLen) {
                maxLen = len;
                start = i - (len - 1) / 2;
            }
        }
        return s.substring(start, start + maxLen);
    }

    private int expandFromCenter(String s, int left, int right) {
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
            left--;
            right++;
        }
        return right - left - 1;
    }
}

该方法是实际工程中最常见的实现,兼顾可读性与性能。


不同算法对比总结

方法 时间复杂度 空间复杂度 思想 优势 劣势
暴力枚举 O(n³) O(1) 完全枚举 易理解 极慢
动态规划 O(n²) O(n²) 状态转移 稳定性高 占用空间多
中心扩展 O(n²) O(1) 对称扩展 简洁高效 理论非最优

如果是初学者,推荐使用中心扩展法,代码简洁、性能足够。

相关推荐
仰泳的熊猫1 小时前
题目1545:蓝桥杯算法提高VIP-现代诗如蚯蚓
数据结构·c++·算法·蓝桥杯
载数而行5201 小时前
复杂度问题
c语言·数据结构·c++·算法·排序算法
WZ188104638691 小时前
LeetCode第20题
算法·leetcode
像素猎人2 小时前
字符串/字符与整型数据的相互转换stoi/stol()和to_string()
c++·算法
吕司2 小时前
LeetCode Hot Code——三数之和
数据结构·算法·leetcode
-海绵东东-2 小时前
图论——代码篇
算法·深度优先·图论
金枪不摆鳍2 小时前
hot100二分查找专题
数据结构·算法
YGGP2 小时前
【Golang】LeetCode 54. 螺旋矩阵
算法·leetcode·矩阵
我命由我123452 小时前
Photoshop - Photoshop 工具栏(68)内容感知移动工具
学习·ui·职场和发展·求职招聘·职场发展·学习方法·photoshop