最长回文子序列(教你由暴力递归改动态规划)

package 左神题目.dp;
//给定--个字符串str,返回这个字符串的最长回文子序列长度比如str = "a12b3c43def2ghi1kpm”
// 最长回文子序列是“1234321" 或者“123c321"返回长度7
import java.util.Scanner;
//做动态规划,最好做出严格表依赖结构,建立空间感,方便优化
//动态规划一定是递归改出来的,但是递归不一定能改动态规划
public class longestPalindromeSubseq {
    //1.反转字符串,然后求两个字符串的最长公共子序列
    //怎么求最长公共子序列,请看上一篇文章
    public static int win1(String s){
        //字符串反转
        String s2 =new StringBuilder(s).reverse().toString();

        char[] str1 = s.toCharArray();
        char[] str2 = s2.toCharArray();

        int N=str1.length;
        int[][] dp = new int[N][N];

        dp[0][0] = str1[0]==str2[0]?1:0;
        for(int j=1;j<N;j++){
            dp[0][j] = str1[0]==str2[j]?1:dp[0][j-1];
        }

        for(int i=1;i<N;i++){
            dp[i][0] = str1[i]==str2[0]?1:dp[i-1][0];
        }

        for(int i=1;i<N;i++){
            for(int j=1;j<N;j++){
                int p1 = dp[i-1][j];
                int p2 = dp[i][j-1];
                int p3 = str1[i]==str2[j]?(1+dp[i-1][j-1]):0;

                dp[i][j] = Math.max(p1,Math.max(p2,p3));
            }
        }
        return dp[N-1][N-1];
    }

    //2.暴力法
    public static int win2(String s){
        if(s==null || s.length()==0){
            return 0;
        }
        char[] str = s.toCharArray();
        return process(str,0,str.length-1);
    }
    public static int process(char[] str,int l,int r){
        if(l==r){
            return 1;
        }
        if(l==r-1){
            return str[l]==str[r]?2:1;
        }
        //不考虑以l开头和以j结尾
        int p1 = process(str,l+1,r-1);
        //只考虑以l开头,不考虑以j结尾
        int p2 = process(str,l,r-1);
        //不考虑以l开头,只考虑以j结尾
        int p3 = process(str,l+1,r);
        //同时考虑以l开头和以j结尾
        int p4 = str[l]==str[r]?(2+process(str,l+1,r-1)):0;

        //p1和p4可以写一起 p4 = str[l]==str[r]?(2+process(str,l+1,r-1)):process(str,l+1,r-1);
        return Math.max(p1,Math.max(p2,Math.max(p3,p4)));
    }

    //3.动态规划
    public static int win3(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;
        }

        //2-N列,从第三条对角线开始填
//        for(int i = 2;i<N;i++){
//            int r=i;
//            //0~N-i行
//            for(int l = 0;l<N-i;l++,r++){
//                int p1 = dp[l+1][r-1]; // --> p1 = process(str,l+1,r-1);
//                int p2 = dp[l][r-1];  // --> p2 = process(str,l,r-1);
//                int p3 = dp[l+1][r]; // -->  p3 = process(str,l+1,r);
//                int p4 = str[l]==str[r]?2+p1:0; // -->p4 = str[l]==str[r]?(2+process(str,l+1,r-1)):0;
//
//                dp[l][r]=Math.max(p1,Math.max(p2,Math.max(p3,p4)));
//            }
//        }

        //行 从下往上填
//        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]?2+p1:0;
//                //p1 p4 ---> p =str[l]==str[r]? 2+dp[l+1][r-1]:dp[l+1][r-1];
//                dp[l][r]=Math.max(p1,Math.max(p2,Math.max(p3,p4)));
//            }
//        }

        for(int l = N-3;l>=0;l--){
            for(int r = l+2;r<N;r++){
                //为什么不要p1了,因为dp[l+1][r]是他的三个方向的最大值,同理dp[l][r-1]也是
                //因为dp[l+1][r-1]是p2的下,p3的左,p2,p3都是大于等于p1的,所以没必要写p1的
                //只用判断一下p1+2
                dp[l][r] = Math.max(dp[l+1][r],dp[l][r-1]);

                if(str[l]==str[r]){
                    dp[l][r] = Math.max(dp[l][r],2+dp[l+1][r-1]);
                }
            }
        }
        return dp[0][N-1];
    }

    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        String s = sc.next();

        long start1 = System.currentTimeMillis();
        System.out.println(win1(s));
        long end1 = System.currentTimeMillis();
        System.out.println("cost time:" + (end1-start1)+"ms");

        long start2 = System.currentTimeMillis();
        System.out.println(win2(s));
        long end2 = System.currentTimeMillis();
        System.out.println("cost time:" + (end2-start2)+"ms");

        long start3 = System.currentTimeMillis();
        System.out.println(win3(s));
        long end3 = System.currentTimeMillis();
        System.out.println("cost time:" + (end3-start3)+"ms");
    }
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
最长回文子序列递归复杂度是O(n^2)。这是因为在求解最长回文子序列的过程中,涉及到对每一个子序列进行判断和计算,而子序列的长度为n。因此,总的时间复杂度为O(n^2)。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [LeetCode:516 最长回文子序列](https://blog.csdn.net/chengda321/article/details/104523971)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [516、最长回文子序列](https://blog.csdn.net/pengchengliu/article/details/92764961)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [西电算法课程期末复习资料.zip](https://download.csdn.net/download/qq_42475914/12732528)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值