1. 题目
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
示例 2:
输入: “cbbd”
输出: “bb”
Related Topics 字符串 动态规划
👍 2467 👎 0
2. 题解
2.1 解法1: 中心扩展法
遍历每一个字符, 分别从 同一位置 i 和 i, i+1 位置进行两边扩散, 寻找最大长度并返回
注意点: 返回的长度计算为 right-left-1
class Solution {
public String longestPalindrome(String s) {
int len = s.length();
if (len < 2) return s;
int start = 0;
int maxLen = 0;
for (int i = 0; i < len; i++) {
int[] odd = centerSpread(s, i, i);
int[] even = centerSpread(s, i, i + 1);
int[] tempMax = odd[1] > even[1] ? odd : even;
if (tempMax[1] > maxLen) {
start = tempMax[0];
maxLen = tempMax[1];
}
}
return s.substring(start, start + maxLen);
}
// 中心扩展寻找最大长度, 返回数组, int[0] 为起始下标, int[1] 为长度
public int[] centerSpread(String s, int left, int right) {
int len = s.length();
while (left >= 0 && right < len) {
if (s.charAt(left) == s.charAt(right)) {
left--;
right++;
} else {
break;
}
}
return new int[]{left + 1, right - left - 1};
}
}
写法2: 每次手动计算初始下标
class Solution {
public String longestPalindrome(String s) {
int len = s.length();
if (len < 2) return s;
int maxLen = 0;
int start = 0;
for (int i = 0; i < len; i++) {
int evenLen = centreSpread(s, i, i);
int oddLen = centreSpread(s, i, i + 1);
int tempLen = Math.max(evenLen, oddLen);
if (maxLen < tempLen) {
start = i - (tempLen - 1) / 2;
maxLen = tempLen;
}
}
return s.substring(start, start + maxLen);
}
// 中心扩展寻找最大长度, 返回回文串最大长度
public int centreSpread(String s, int left, int right) {
int len = s.length();
while (left >= 0 && right < len) {
if (s.charAt(left) == s.charAt(right)) {
left--;
right++;
} else {
break;
}
}
return right - left - 1;
}
}
2.2 解法2: 暴力解法
每次长度大于 2 的 i 和 j , 判断 i…j 是否为回文串, 若是, 且大于当前最大长度 maxLen, 则更新最大长度及起始下标
class Solution {
public String longestPalindrome(String s) {
int begin = 0;
int maxLen = 1;
int len = s.length();
for (int i = 0; i < len - 1; i++) {
for (int j = i + 1; j < len; j++) {
if (maxLen < j - i + 1 && isValid(s, i, j)) {
begin = i;
maxLen = j - i + 1;
}
}
}
return s.substring(begin, begin + maxLen);
}
// 判断下标 left到right 是否为回文串
public boolean isValid(String s, int left, int right) {
if (left > right) return false;
while (left < right) {
if (s.charAt(left) != s.charAt(right)) {
return false;
}
left++;
right--;
}
return true;
}
}
2.3 解法3: 动态规划
public class Solution {
public String longestPalindrome(String s) {
int len = s.length();
if (len < 2) {
return s;
}
int maxLen = 1;
int begin = 0;
// dp[i][j] 表示 s[i..j] 是否是回文串
boolean[][] dp = new boolean[len][len];
// 初始化:所有长度为 1 的子串都是回文串
for (int i = 0; i < len; i++) {
dp[i][i] = true;
}
char[] charArray = s.toCharArray();
// 递推开始
// 先枚举子串长度
for (int L = 2; L <= len; L++) {
// 枚举左边界,左边界的上限设置可以宽松一些
for (int i = 0; i < len; i++) {
// 由 L 和 i 可以确定右边界,即 j - i + 1 = L 得
int j = L + i - 1;
// 如果右边界越界,就可以退出当前循环
if (j >= len) {
break;
}
if (charArray[i] != charArray[j]) {
dp[i][j] = false;
} else {
if (j - i < 3) {
dp[i][j] = true;
} else {
dp[i][j] = dp[i + 1][j - 1];
}
}
// 只要 dp[i][L] == true 成立,就表示子串 s[i..L] 是回文,此时记录回文长度和起始位置
if (dp[i][j] && j - i + 1 > maxLen) {
maxLen = j - i + 1;
begin = i;
}
}
}
return s.substring(begin, begin + maxLen);
}
}
具体参考: 官方题解