动态规划五部曲
1、确定dp数组以及下标的含义
在最长回文子串中 dp数组为布尔类型的dp[i][j] :表示区间范围[i,j](左闭右闭)的子串是否为回文子串,如果是dp[i][j]为ture,否则为false
2、确定递推公式
整体上分两种 一种是s[i]和s[j]不相等 则肯定不是
一种是s[i]和s[j]相等
细分:
下标 i和j相同 例如:a
下标i和j相差为一例如:aa
下标i和j相差大于1 例如:cabac
此时s[i]和s[j]已经相同了 我们只需要看s[i+1][j-1]是否满足即可
3、dp数组如何初始化
dp[i][j]初始化为false。
4、确定遍历顺序
如果这矩阵是从上到下,从左到右遍历,那么会用到没有计算过的dp[i + 1][j - 1],也就是根据不确定是不是回文的区间[i+1,j-1],来判断了[i,j]是不是回文,那结果一定是不对的。
所以一定要从下到上,从左到右遍历,这样保证dp[i + 1][j - 1]都是经过计算的。
有的代码实现是优先遍历列,然后遍历行,其实也是一个道理,都是为了保证dp[i + 1][j - 1]都是经过计算的。
5、举例推导dp数组
class Solution {
public String longestPalindrome(String s) {
int length=s.length();
int left=0;
int right=0;
int maxLength=0;
boolean[][] dp=new boolean[length][length];
for(int i=0;i<length;i++){
for(int j=i;j<length;j++){
dp[i][j]=false;
}
}
for(int i=length-1;i>=0;i--){
for(int j=i;j<length;j++){
if(s.charAt(i)==s.charAt(j)){
if(j-i<=1){
dp[i][j]=true;
}else if(dp[i+1][j-1]){
dp[i][j]=true;
}
}
if(dp[i][j]&&j-i+1>maxLength){
maxLength=j-i+1;
left=i;
right=j;
}
}
}
String ans=s.substring(left, right+1);
return ans;
}
}
- 时间复杂度:O(n^2)
- 空间复杂度:O(n^2)
双指针法:
首先确定回文串,就是找中心然后想两边扩散看是不是对称的就可以了。
在遍历中心点的时候,要注意中心点有两种情况。
一个元素可以作为中心点,两个元素也可以作为中心点。
这两种情况可以放在一起计算,但分别计算思路更清晰
class Solution {
int left=0;
int right=0;
int maxLength=0;
public String longestPalindrome(String s) {
for(int i=0;i<s.length();i++){
extend(s,i,i,s.length());
extend(s,i,i+1,s.length());
}
return s.substring(left,right+1);
}
void extend(String s ,int i, int j, int n){
while(i>=0&&j<n&&s.charAt(i)==s.charAt(j)){
if(j-i+1>maxLength){
left=i;
right=j;
maxLength=j-i+1;
}
i--;
j++;
}
}
}
- 时间复杂度:O(n^2)
- 空间复杂度:O(1)