如何构造dp数组
一开始想到的是dp[i][j]表示索引i到索引j之间的最长回文子串,然后我们的目标是d[0][length-1];
基于此定义,如果要构造状态转移,会发现异常困难;
题解中采用的是dp[i][j]表示索引i到索引j之间的字符串是否为回文子串
即第一个的返回值为String,第二个的返回值为boolean
基于第二个定义,构造状态转移会非常容易
dp[i][j]=dp[i+1][j-1] && (st[i]==st[j])
即如果一个字符串本身为回文串,那么两边各加上相同的单个字符串,整体必然是回文串;
同时考虑base case
当i==j时,dp[i][j]=true;
当i+1==j时,dp[i][j]=true;
当j-i>=2时,就可以直接使用状态转移
class Solution {
public String longestPalindrome(String s) {
char[] st=s.toCharArray();
int length=st.length;
boolean[][] dp=new boolean[length][length];
// base case赋值
for(int i=0;i<length;i++) {
dp[i][i]=true;
if(i+1<length){
dp[i][i+1]=(st[i]==st[i+1]);
}
}
//错误的遍历方式
// for(int i=0;i<length;i++){
// for(int j=i;j<length;j++){
// if(j-i>1) {
// dp[i][j]=dp[i+1][j-1]&&(st[i]==st[j]);
// }
//
// }
// }
// 状态转移
int tail=0;
int front=0;
for(int len=3;len<=length;len++) {
for(front =0;front<length;front++) {
tail=front+len-1;
if(tail>=length) {
break;
}
dp[front][tail]=dp[front+1][tail-1]&&(st[front]==st[tail]);
}
}
// 找到最大回文子串
int max=0;
for(int i=0;i<length;i++){
for(int j=i;j<length;j++){
if(dp[i][j]==true){
if(j-i>=max){//加上等于号防止进不去
max=j-i;
front=i;
tail=j;
}
}
}
}
// 将其截取
String out="";
for(int i=front;i<=tail;i++){
out=out+st[i];
}
return out;
}
}
一开始我用的遍历是i和j都用for循环,其中i从0开始,j从i开始
但这样有问题
比如dp[0][5]需要用到dp[1][4]
而算到前者时,后者还未算到,所以如果按初始值false来算就会出错
因此这样的遍历方式是有问题的
基于以上问题
正确的遍历方式是先考虑短的子串,再逐渐将其加长;
外循环用子串长度作为变量
内循环用左索引作为变量
最后dp数组都赋值结束之后,就可以从值为true的元素中找出长度最长的子串
总结
本题中的dp数组的构造非常巧妙
不直接求出回文子串
而是判断是否为回文子串