题目
给你一个字符串 s
,找到 s
中最长的回文子串。
- 子串(substring):原始字符串的一个连续子集;
- 子序列(subsequence):原始字符串的一个子集。
示例1
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例2
输入:s = "a"
输出:"a"
解题思路
动态规划需要有状态和状态转移方程:
-
状态: d p [ i ] [ j ] dp[i][j] dp[i][j] 表示子串 s [ i ⋯ j ] s[i\cdots j] s[i⋯j] 是否为回文子串
-
状态转移方程:
d p [ i ] [ j ] = ( s [ i ] = = s [ j ] ) ∧ ( d p [ i + 1 ] [ j − 1 ] ) dp[i][j] = (s[i]==s[j]) \land (dp[i+1][j-1]) dp[i][j]=(s[i]==s[j])∧(dp[i+1][j−1]) -
边界条件: i + 1 i+1 i+1 与 j − 1 j-1 j−1 不构成区间,即 ( j − 1 ) − ( i + 1 ) < 1 (j-1)-(i+1)<1 (j−1)−(i+1)<1,整理得 j − i + 1 < 4 j-i + 1<4 j−i+1<4
意义:表明当 s [ i ⋯ j ] s[i\cdots j] s[i⋯j] 的长度为2或3时,不用检查子串是否回文,因为其本身已经是最基本的子串
-
初始化: d p [ i ] [ j ] = t r u e dp[i][j]=true dp[i][j]=true(可省略,因为判断时没不到)
-
输出:在得到一个状态的值为 true 的时候,记录起始位置和长度,填表完成以后再截取
详解
有以下字符串:
字符 | b | a | b | a | a |
---|---|---|---|---|---|
下标 | 0 | 1 | 2 | 3 | 4 |
由于 d p [ i ] [ j ] dp[i][j] dp[i][j]参考了 d p [ i + 1 ] [ j − 1 ] dp[i+1][j-1] dp[i+1][j−1]的值,即表格中左下方的值,故
- 先升序填列
- 再升序填行
左边界i\右边界j | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
0 | TRUE | FALSE | TRUE | FALSE | FALSE |
1 | TRUE | FALSE | TRUE | FALSE | |
2 | TRUE | FALSE | FALSE | ||
3 | TRUE | TRUE | |||
4 | TRUE |
代码
class Solution {
public String longestPalindrome(String s) {
int len = s.length();
if(len == 1) {
return s;
}
int maxLen = 1;
int start = 0;
boolean[][] dp = new boolean[len][len];
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){ // 子串长度为2或3
dp[i][j] = true; // 必为回文子串
} else { // 子串长度大于3
dp[i][j] = dp[i+1][j-1]; // 与其之前的子串相关
}
}
if(dp[i][j] && (j-i+1 > maxLen)) {
maxLen = j - i + 1;
start = i;
}
}
}
return s.substring(start, start + maxLen);
}
}