题目描述
给你一个字符串 s,找到 s 中最长的回文子串。
示例1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例2:
输入:s = "cbbd"
输出:"bb"
示例3:
输入:s = "a"
输出:"a"
思路
动态规划。=> 使用dp[i][j]表示从i到j是否为回文。
如果希望dp[i][j]为回文的话,那么首先字符str[i] == str[j]并且dp[i + 1][j - 1]也是回文。
注意,如果str[i] == str[j] 且i到j一共只有三个字符,那么他们就是回文。
因此,步骤如下:
- 初始化dp数组,对角线全为true;
- j指向的是后面的字符,因此外循环遍历j,从j = 1开始;
- i指向前面的字符,内循环遍历i, 每次都是从i = 0 开始;
- 如果str[j] != str[i] 说明从i到j必定不是回文,那么i 往后移动,终止条件为i < j;
- 如果str[j] == str[i] 此时有两种情况
- 只有三个字符,直接设置为true
- 大于三个字符,那么如果dp[i + 1][j - 1]也为true, 那么dp[i][j]也为true
- 当dp[i][j]为true时,判断此时的回文长度,如果大于之前的最大回文长度,那么最后要的索引就更新为i和j,否则不更新。
代码
class Solution {
public String longestPalindrome(String s) {
int len = s.length();
// 长度为1,已经是回文了
if (len == 1) {
return s;
}
int start = 0;
int end = 0;
int maxLength = 0;
boolean[][] dp = new boolean[len][len];
// 初始化
for (int i = 0; i < len; i++) {
dp[i][i] = true;
}
// 从j开始遍历
for (int j = 1; j < len; j++) {
for (int i = 0; i < j; i++) {
if (s.charAt(i) != s.charAt(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][j]为true这一条件
if (dp[i][j] && j - i + 1 > maxLength) {
start = i;
end = j;
maxLength = j - i + 1;
}
}
}
}
return s.substring(start, end + 1);
}
}
注意:
- 最大长度的计算方法:j - i + 1,记得要+ 1, 例如i = 0, j = 2, 长度为3,即 2 - 0 + 1;
- 只有当dp[i][j]为true时,才去尝试修改最大长度,即
if (dp[i][j] && j - i + 1 > maxLength) { start = i; end = j; maxLength = j - i + 1; }
pss: 该题可用“马拉车算法”,将复杂度降到O(n),但是非常复杂,面试想不出来,因此只用动态规划即可。