LeetCode 第5题:最长回文子串
LeetCode 第5题:最长回文子串
题目描述
给你一个字符串 s,找到 s 中最长的回文子串。
难度
中等
题目链接
https://leetcode.cn/problems/longest-palindromic-substring/
示例
示例 1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd"
输出:"bb"
提示
- 1 <= s.length <= 1000
- s 仅由数字和英文字母组成
解题思路
方法一:中心扩展法
回文串是以中心对称的,可以从每个位置开始,向两边扩展,找到以该位置为中心的最长回文串。
关键点:
- 回文串的中心可能是一个字符,也可能是两个字符
- 需要考虑奇数长度和偶数长度的回文串
- 对每个可能的中心都要尝试扩展
具体步骤:
- 遍历字符串的每个字符作为中心点
- 对每个中心点:
- 尝试奇数长度扩展(一个字符为中心)
- 尝试偶数长度扩展(两个字符为中心)
- 记录最长的回文子串
时间复杂度:O(n²)
空间复杂度:O(1)
方法二:动态规划
使用动态规划可以避免重复计算。定义dp[i][j]表示s[i..j]是否为回文串。
状态转移方程:
- dp[i][j] = (s[i] == s[j]) && (j - i < 3 || dp[i+1][j-1])
代码实现
C# 实现(中心扩展法)
public class Solution {
public string LongestPalindrome(string s) {
if (string.IsNullOrEmpty(s)) return "";
int start = 0, maxLength = 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 > maxLength) {
maxLength = len;
start = i - (len - 1) / 2;
}
}
return s.Substring(start, maxLength);
}
private int ExpandAroundCenter(string s, int left, int right) {
// 从中心向两边扩展,直到不能形成回文
while (left >= 0 && right < s.Length && s[left] == s[right]) {
left--;
right++;
}
// 返回回文串的长度
return right - left - 1;
}
}
C# 实现(动态规划)
public class Solution {
public string LongestPalindrome(string s) {
int n = s.Length;
bool[,] dp = new bool[n, n];
int maxLength = 1;
int start = 0;
// 所有单个字符都是回文
for (int i = 0; i < n; i++) {
dp[i, i] = true;
}
// 检查长度为2的子串
for (int i = 0; i < n - 1; i++) {
if (s[i] == s[i + 1]) {
dp[i, i + 1] = true;
start = i;
maxLength = 2;
}
}
// 检查长度大于2的子串
for (int len = 3; len <= n; len++) {
for (int i = 0; i <= n - len; i++) {
int j = i + len - 1;
if (s[i] == s[j] && dp[i + 1, j - 1]) {
dp[i, j] = true;
if (len > maxLength) {
start = i;
maxLength = len;
}
}
}
}
return s.Substring(start, maxLength);
}
}
代码详解
中心扩展法:
ExpandAroundCenter
方法:- 从给定的中心向两边扩展
- 返回找到的回文串长度
- 主方法中:
- 对每个位置尝试奇数和偶数长度的扩展
- 更新最长回文串的起始位置和长度
动态规划:
- 初始化:
- 单个字符都是回文
- 检查长度为2的子串
- 状态转移:
- 检查更长的子串
- 使用dp数组记录子串是否为回文
执行结果
中心扩展法:
- 执行用时:76 ms
- 内存消耗:36.5 MB
动态规划:
- 执行用时:124 ms
- 内存消耗:37.8 MB
总结与反思
- 这是一道经典的字符串处理题目,考察了:
- 回文串的性质
- 动态规划的应用
- 中心扩展的思想
- 两种解法比较:
- 中心扩展法实现简单,空间复杂度低
- 动态规划思路清晰,但空间复杂度较高
- 优化思路:
- 可以用Manacher算法进一步优化到线性时间复杂度
- 预处理可以减少边界判断
相关题目
- LeetCode 第516题:最长回文子序列
- LeetCode 第647题:回文子串
- LeetCode 第9题:回文数
- LeetCode 第214题:最短回文串