力扣5. 最长回文子串
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
分析
1. 暴力法。
时间复杂度O(n³)
,超时。
2. 动态规划。
既然是回文字符串,那么如果内层的字母是回文的,并且外层的也是回文,整个字符串就是回文字符串。
我们就得到了这么一个关系:
从短到长,第i
个字母到第j
个字母的状态
dp[i][j] = (str[i] == str[j]) && dp[i+1][j-1]
也就是,如果第i
个字母等于第j
个字母,而且他们的内层字母也是回文,那么i
到j
就是个回文串。
考虑一下边界问题,如果是aba
这种字符串,当i
和j
中间只剩一个字母时,就可以不看dp[i+1][j-1]
了,因为一个字母肯定是回文串。
所以我们就得到了这么一个伪代码:
for j in [1, n):
for i in [0, j):
if(s[i] != s[j]):
dp[i][j] = false;
else:
if(j-1 - (i+1) +1 <2):
dp[i][j] = true;
else:
dp[i][j] = dp[i+1][j-1];
if(i到j是回文串 && i到j距离大于max):
更新max;
更新起始点;
代码
按照上文的伪代码,我们可以写出实际代码。
class Solution {
public String longestPalindrome(String s) {
int n = s.length();
//特殊情况
if(n == 1) return s;
if(n == 0) return "";
//初始化dp数组
boolean[][] dp = new boolean[n][n];
for(int i = 0; i<n; i++){
dp[i][i] = true;
}
int maxLen = 1;
//记录最长回文串的起始位置
int begin = 0;
char[] array = s.toCharArray();
//双重循环判断是否是回文
for(int j = 1; j<n; j++){
for(int i = 0; i<j; i++){
//第i个字符和第j个字符不同,说明i到j不是回文。
if(array[i]!= array[j]){
dp[i][j] = false;
}else{
if((j-1) - (i+1) +1<2){
//说明i和j中间只剩一个字符,而且i和j的字符相同,一定是回文。
dp[i][j] = true;
}else{
//i和j中间还有很多字符,ij是否是回文需要看内层字符串。
dp[i][j] = dp[i+1][j-1];
}
}
//记录最长回文串位置。
//要求i到j是回文串,而且长度大于原来的最大长度
if(dp[i][j] == true && (j-i+1)>maxLen){
maxLen = j-i+1;
begin = i;
}
}
}
return s.substring(begin,begin+maxLen);
}
}