目录
从暴力递归到动态规划
给定一个字符串str,返回这个字符串的最长回文子序列长度比如:
str ="a12b3c43def2ghi1kpm"
最长回文子序列是“1234321”或者“123c321,返回长度7
暴力递归
在L~R上从两边向中间遍历尽可能多的找出 str[L] == str [R]。
无非遇见三种情况:L向右移,R向左移,L向右移同时R向左移。
-
当L==R时,返回1;
-
当L==R-1时,若str[L] == str [R],返回2;否则返回1
public static int longestPalindromeSubsequence (String str) {
if (str == null || str.length()==0) {
return -1;
}
char[] chars = str.toCharArray();
return lps(chars,0, chars.length-1);
}
public static int lps (char[] str, int L, int R) {
if (L == R) {
return 1;
}
if (L == R-1) {
return str[L]==str[R] ? 2 : 1;
}
int p1 = lps(str,L+1,R-1);
int p2 = lps(str,L+1,R);
int p3 = lps(str,L,R-1);
int p4 = str[L]==str[R] ? 2+lps(str,L+1,R-1) : 0;
return Math.max(Math.max(p1,p2),Math.max(p3,p4));
}
动态规划
从暴力递归解法看出,L和R为动态规划中的全过程变量,因此可以建立一张二维的缓存表。
L的取值范围:0~str.length()-1;
R的取值范围:0~str.length()-1;
//动态规划
public static int longestPalindromeSubsequence1 (String str) {
if (str == null || str.length()==0) {
return -1;
}
char[] chars = str.toCharArray();
int N = chars.length;
int dp[][] = new int[N][N];
for (int i = 0; i < N -1; i++) {
//L==R时;
dp[i][i] = 1;
if (i+1<N) {
//L==R-1时
dp[i][i + 1] = chars[i] == chars[i + 1] ? 2 : 1;
}
}
for (int R = 2; R < N; R++) {
for (int L = R-2; L >= 0; L--) {
//可以省略p1
int p1 = dp[L+1][R-1];
int p2 = dp[L+1][R];
int p3 = dp[L][R-1];
int p4 = chars[L]==chars[R] ? (2+dp[L+1][R-1]) : 0;
dp[L][R] = Math.max(Math.max(p1,p2),Math.max(p3,p4));
}
}
return dp[0][N-1];
在对dp表赋值时,对p1的判断是可以省略的,因为p2和p3在此之前已经和p1进行过最大值比较,p2和p3的值不小于p1,故p1在这里不发挥作用。
测试:
public static void main(String[] args) {
System.out.println(longestPalindromeSubsequence("a122egdfg3h42q1"));
System.out.println(longestPalindromeSubsequence1("a122egdfg3h42q1"));
}
//输出
7
7