动态规划问题-求最长回文子序列长度

题目描述:
给定一个字符串str,返回这个字符串的最长回文子序列长度
比如 str = “a12b3c43def2ghi1kpm”
最长回文子序列是 “123c321” 或者1234321 返回长度为7

暴力递归方法
思路解析:就是分情况考虑
回文串 开始位置不算, 结束位置不算
开始位置算, 结束位置不算
开始位置不算, 结束位置算
开始位置算, 结束位置算

实现代码

public static int lpsl1(String s) {
   if (s == null || s.length() == 0) {
      return 0;
   }
   char[] str = s.toCharArray();
   return f(str, 0, str.length - 1);
}

// str[L..R]最长回文子序列长度返回
public static int f(char[] str, int L, int R) {
   if (L == R) {
      return 1;
   }
   if (L == R - 1) {
      return str[L] == str[R] ? 2 : 1;
   }
   int p1 = f(str, L + 1, R - 1);
   int p2 = f(str, L, R - 1);
   int p3 = f(str, L + 1, R);
   int p4 = str[L] != str[R] ? 0 : (2 + f(str, L + 1, R - 1));
   return Math.max(Math.max(p1, p2), Math.max(p3, p4));
}

由暴力递归转动态规划
思路解析
通过画图知道,每一行依赖于它下边的,左边的格子,左边下角的格子
所以 应该从下往上填,但是每一行又是从左往右填写
所以列L 是从N-3出发(N-1和N-2已经填完了);而每一行R 是从L+2开始填写

代码实现

public static int lpsl2(String s) {
   if (s == null || s.length() == 0) {
      return 0;
   }
   char[] str = s.toCharArray();
   int N = str.length;
   int[][] dp = new int[N][N];
   dp[N - 1][N - 1] = 1;
   for (int i = 0; i < N - 1; i++) {
      dp[i][i] = 1;
      dp[i][i + 1] = str[i] == str[i + 1] ? 2 : 1;
   }
   for (int L = N - 3; L >= 0; L--) {
      for (int R = L + 2; R < N; R++) {
            int p1 = dp[L + 1, R - 1];
            int p2 = dp[L, R - 1];
            int p3 = dp[L + 1, R];
            int p4 = str[L] != str[R] ? 0 : dp[L + 1, R - 1]);
            dp[L][R] = Math.max(Math.max(p1, p2), Math.max(p3, p4));
      }
   }
   return dp[0][N - 1];
}

优化版本
因为每一个数都依赖它左边的,下边的,左下的 并求最大值
那么这样一来其实左下可以省略 即最大值不会比左下小

public static int lpsl2(String s) {
   if (s == null || s.length() == 0) {
      return 0;
   }
   char[] str = s.toCharArray();
   int N = str.length;
   int[][] dp = new int[N][N];
   dp[N - 1][N - 1] = 1;
   for (int i = 0; i < N - 1; i++) {
      dp[i][i] = 1;
      dp[i][i + 1] = str[i] == str[i + 1] ? 2 : 1;
   }
   for (int L = N - 3; L >= 0; L--) {
      for (int R = L + 2; R < N; R++) {
         dp[L][R] = Math.max(dp[L][R - 1], dp[L + 1][R]);
         if (str[L] == str[R]) {
            dp[L][R] = Math.max(dp[L][R], 2 + dp[L + 1][R - 1]);
         }
      }
   }
   return dp[0][N - 1];
}

总结
这种属于范围尝试模型,
范围尝试模型特别注重开头和结尾共同结合的情况分类—即开头如何如何,结尾如何来靠近
而上节课说的最长公共子序列问题属于样本对应模型,比较注重结尾分类情况

你可能感兴趣的:(算法,动态规划,算法)