题目描述
最长回文子串
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
示例 2:
输入: “cbbd”
输出: “bb”
解题思路
看到这道题的时候,第一眼能想到的方法就是暴力破解,枚举每个子串,看它是不是回文子串,并且记录最长的回文子串。它的时间复杂度为O(N^3),空间复杂度为O(N),这里N指的是数组的长度。
时间复杂度为O(N^3)的原因是使用两层for循环找到所有的子串,然后再使用一层for循环判断该子串是否为回文子串。这里我们不讨论它的实现。
接下来我们讨论如何使用动态规划解决这个问题。
动态规划解题模板
动态规划解题是有章可循的,我们可以将其分为以下五步:
(1)确定dp数组元素的含义
(2)确定状态转移方程
(3)确定初始值
(4)根据初始值和状态转移方程确定dp数组元素的值
(5)考虑空间压缩
我们直接结合代码来分析:
第一步:确定dp数组元素的含义
- 对于最长回文子串我们可以定义数组dp[i][j]表示区间[i,j]是否为回文子串。
第二步:确定状态转移方程
- 如果 s[i]!=s[j] ,将dp[i][j]置为false;
- 如果 s[i]==s[j],并且dp[i+1][j-1]==true,我们则将dp[i][j]置为true;
- 考虑特殊情况如 “aa”, “aba” ,当s[i]==s[j]的时候可以直接将其置为true,因此可以得到 s[i]==s[j]&&j-i<3时 dp[i][j]=true;
第三步:确定初始值
- 每一个元素都为一个回文子串,因此对于 0<i<s.length(),dp[i][i]=true;
第四步:根据初始值和状态方程确定数组元素的值
- 这里使用两层for循环将确定每个子串是否为回文。
第五步:考虑空间压缩
代码实现
class Solution {
public String longestPalindrome(String s) {
//判非
if(s==null||s.length()<=1){
return s;
}
int len=s.length();
char[] arr=s.toCharArray();
//确定数组元素含义
boolean dp[][]=new boolean[len][len];
//初始化
for(int i=0;i<len;i++){
dp[i][i]=true;
}
int maxLen=1;//记录最大回文子串的长度
int start=0;//记录最大回文串的起始坐标
//根据初始值和状态方程确定数组元素的值
for(int j=1;j<len;j++){
for(int i=0;i<j;i++){
if(arr[i]==arr[j]){
if(j-i<3||dp[i+1][j-1]){
dp[i][j]=true;
int l=j-i+1;
if(maxLen<l){//判断长度
start=i;
maxLen=l;
}
}
}else{
dp[i][j]=false;
}
}
}
return s.substring(start,start+maxLen);
}
}