从直接递归入手改为动态规划(官方的动态转移方程答案看不懂)
当某个字符串是回文序列时,考虑其内部字符串是否还是回文序列,可以看到每次遍历时的步骤是一致的,可以通过递归的方式求解
直接递归求解(此代码题解超时)
public static int process(char[] strArr,int l,int r){
//base case
//当只剩余一个字符时
if(l==r){
return 1;
}
//当只剩余2个字符时
if(l==r-1){
return strArr[l]==strArr[r]?2:1;
}
//any 考虑结尾情况分类
//a.最终的最长回文子序列不以l r结尾
//需要去考虑下一个位置
int p1=process(strArr,l+1,r-1);
//b.最终结尾以l结尾 不以r结尾
//r位置向下偏移 考虑有可能产生回文的情况
int p2=process(strArr,l,r-1);
//c.最终结尾以r结尾 不以l结尾
//l位置向内偏移 考虑下一个有可能产生回文的情况
int p3=process(strArr,l+1,r);
//d.均以l r结尾
//判断当前位置是否可能产生结果
//l r位置均往内偏移 寻找下一个可能产生回文的情况
int p4= strArr[l]==strArr[r]? (2+process(strArr,l+1,r-1)):0;
//收集以上4种情况取最大值
return Math.max(Math.max(p1,p2),Math.max(p3,p4));
}
改为递归
从递归过程很明显看到有2个变量l和r,故
-
构建二维dp表,大小为N*N
int[][] dp=new int[N][N];
-
base case
根据递归步骤 dp表对角线上的格子都=1,对角线斜向右上方的斜线值也可以得到
dp[i][i]=1; dp[i][i+1]=str[i]==str[i+1]?2:1;
-
确定返回值
return dp[0][N-1];
-
一般性位置填表
确定填表顺序。根据递归过程可以看到
dp[i][j]位置依赖于 dp[i+1][j-1] dp[i][j-1] dp[i+1][j]
所有一般性位置(i,j)依赖于左下角的位置,所有填表顺序为从下往上,从左往右
public static int processDP(char[] strArr) { //1.构建dp表 int N=strArr.length; int[][] dp=new int[N][N]; //[l,r] //2.base case for(int i=0;i<N;i++){ dp[i][i]=1; } for(int i=0;i<N-1;i++){ dp[i][i+1]= strArr[i]==strArr[i+1]?2:1; } //4.填表 //确定顺序 for(int i=N-3;i>=0;i--){ for(int j=i+2;j<N;j++){ int p1=dp[i+1][j-1]; int p2=dp[i][j-1]; int p3=dp[i+1][j]; int p4= strArr[i]==strArr[j]? (2+dp[i+1][j-1]):0; dp[i][j]=Math.max(Math.max(p1,p2),Math.max(p3,p4));; } } //3.返回值 return dp[0][N-1]; }