动态规划(JAVA)-最长回文子序列

动态规划(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];
    }

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