(LeetCodeHot100)5. 最长回文子串——longest-palindromic-substring

5. 最长回文子串——longest-palindromic-substring

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

示例 1:

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

示例 2:

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

提示:

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

要解决这个问题,我们需要找到字符串中最长的回文子串。回文子串是指从左到右和从右到左读都相同的连续字符序列。

解题思路

中心扩展法是解决最长回文子串问题的高效方法,其核心思路是:

  1. 回文子串有两种形式:长度为奇数(如 “aba”)和长度为偶数(如 “abba”)
  2. 对于每个字符,我们将其视为奇数长度回文的中心,向两边扩展检查
  3. 对于每对相邻字符,我们将其视为偶数长度回文的中心,向两边扩展检查
  4. 记录扩展过程中找到的最长回文子串

这种方法的时间复杂度为 O (n²),空间复杂度为 O (1),适合处理长度不超过 1000 的字符串。

Java 实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class LongestPalindromicSubstring {
public String longestPalindrome(String s) {
if (s == null || s.length() == 0) {
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 maxLen = Math.max(len1, len2);

// 更新最长回文子串的起始和结束索引
if (maxLen > end - start) {
start = i - (maxLen - 1) / 2;
end = i + maxLen / 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++;
}
// 当退出循环时,left和right已经超出回文范围,所以实际长度是right - left - 1
return right - left - 1;
}
}

代码解释

  1. longestPalindrome方法:
    • 遍历字符串中的每个字符
    • 对每个字符,分别以它为中心(奇数长度)和以它与下一个字符为中心(偶数长度)调用扩展方法
    • 比较两种扩展得到的回文长度,更新最长回文子串的起始和结束索引
  2. expandAroundCenter方法:
    • 接收左右两个指针作为中心
    • 当左右指针指向的字符相等且未越界时,继续向两边扩展
    • 返回找到的回文子串的长度
  3. 最后通过substring方法根据记录的起始和结束索引,返回最长回文子串

(start, end + 1)为什么要加一

String.substring(int start, int end) 方法的第二个参数 end不包含在截取结果中的