动态规划(JAVA)-最长回文子序列
leetcode 题目连接
给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。
一个简易思路,将字符串s翻转,当做第二个参数,求出来的最长公共子序列就是字符串s的最长回文子序列,对于如何求最长公共子序列的可以看我这篇文章
解题思路
分析
- 首先确定可变参数(i,j),假设回文子序列的中点的下角标为 a,a有可能是一个,也有可能是两个,比如"d12bb21c"中,a的值既是3,也是4,对应的"bb",那么有"i<=a"和"j>=a";
- 确定特殊值:
-
- 当i==j的时候,可以确定当前i或者j所在的字符为一个回文子序列,只不过长度是1,对应的是a为一个值时;
-
- 当i==j+1的时候,且两个位置所在的字符相等时,i与j的两个字符共同组成一个回文子序列,如上面"d12bb21c"中的"bb",此时长度为2;
- 确定可能出现的情况,按照可变参数确定,现假设i对应的字符为n,j对应的字符为m
-
- 回文子序列即不以n开头,也不以m结尾,对应的参数变化就是:(i+1,j-1);
-
- 回文子序列不以n开头,但是以m结尾,对应的参数变化就是:(i+1,j);
-
- 回文子序列以n开头,不以m结尾,对应的参数变化就是:(i,j-1);
-
- 回文子序列即以n开头,也以m结尾,对应的参数变化也是:(i+1,j-1),但是需要额外判断m==n;
- 题目要求最大回文子序列,那么对四种情况的结果取max值即可
确定好思路后,开始尝试写代码
public static int longestPalindromeSubseq(String s) {
char[] chars = s.toCharArray();
return recursion(chars,0,chars.length-1);
}
private static int recursion(char[] chars, int i, int j) {
if(i==j){
return 1;
}
if(i==j-1){
return chars[i]==chars[j]?2:1;
}
int res1 = recursion(chars,i+1,j-1);
int res2 = recursion(chars,i,j-1);
int res3 = recursion(chars,i+1,j);
int res4 = chars[i]==chars[j]?2+res1:0;
return Math.max(Math.max(res1,res4),Math.max(res2,res3));
}
转换dp
public static int longestPalindromeSubseq(String s) {
char[] chars = s.toCharArray();
int n = chars.length;
int[][] dp = new int[n][n];
//先约束边界 dp[L][R]
dp[n-1][n-1] = 1;
//约束的下边界,那就从上边界开始,直至下边界的前一位
//此处初始化对角线以及倒数第二的对角线,在上面的分析中已经提到过范围约束,不可能存在对角线左下方的情况
for (int i = 0; i < n-1; i++) {
dp[i][i]=1;
dp[i][i+1] = chars[i]==chars[i+1]?2:1;
}
//下面是dp矩阵,首先对角线以及#对应的值都已经在上面的循环赋值了,所以求值需要设置的L的边界是&符号所在的位置,
//同时根据矩阵可知,对于R来说,%符号所在的位置是没有值的开始位置,所以R的开始位置是L+2,最大值是n
// 0 1 2 3 4 5 6 R
// 0 1 # %
// 1 1 #
// 2 1 #
// 3 1 #
// 4 1 # &
// 5 1 #
// 6 1
// L
for (int i = 0; i < n - 3; i++) {
for (int j = i+2; j < n; j++) {
//将原本的递归,转换为动态规划
// int res1 = process(chars,i+1,j-1);
int res1 = dp[i+1][j-1];
// int res2 = process(chars,i,j-1);
int res2 = dp[i][j-1];
// int res3 = process(chars,i+1,j);
int res3 = dp[i+1][j];
// int res4 = chars[i]==chars[j]?2+res1:0;
int res4 = chars[i]==chars[j]?2+res1:0;
//当前要求的值是dp[i][j]
dp[i][j] = Math.max(Math.max(res1,res4),Math.max(res2,res3));
}
}
return dp[0][n-1];
}