LeetCoed:1143. 最长公共子序列问题

本文探讨了如何从暴力递归转换到动态规划解决两个字符串的最长公共子序列问题,并介绍了如何利用此方法找到最长回文子序列。通过实例代码详细解释了两种方法的实现过程,展示了动态规划在效率上的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

题目(从暴力递归到动态规划)

暴力递归

动态规划

最长回文子序列


https://leetcode.com/problems/longest-common-subsequence/

题目(从暴力递归到动态规划)

给定两个数组str1和str2,返回它们的公共最长子序列长度。

例:str1 = "as123d24"

str2 = "dsc12t32h4"

最长公共子序列为:s1234d,长度为6.

暴力递归

思路:遍历到每一个字符可能出现的每一种情况,我们可以对str1[0....i]和str2[0....j]逐一比较;

有四种情况:

  • i==0且j==0;若此时str1==str2,返回1,否则0;

  • i==0但j!=0;若此时str1==str2,返回1,否则递归 j-1;

  • i!=0但j==0;若此时str1==str2,返回1,否则递归 i-1;

  • i!=0且j!=0;这里又可以细分为三种情况:i减小;j减小;i和j同时减小。

代码如下

	//暴力递归
    public static int longestCommonSubsequence (String str1, String str2) {
        if (str1==null || str2==null || str1.length()==0 || str2.length()==0) {
            return 0;
        }
        return 	process(str1.toCharArray(),str2.toCharArray(),str1.length()-1,str2.length()-1);
    }

    public static int process (char[] str1, char[] str2, int i, int j) {
        if (i == 0 && j ==0) {
            return str1[i] == str2[j] ? 1 : 0;
        } else if (i == 0) {
            if (str1[i] == str2[j]) {
                return 1;
            } else {
                return process(str1,str2,i,j-1);
            }
        } else if (j == 0) {
            if (str1[i] == str2[j]) {
                return 1;
            } else {
                return process(str1,str2,i-1,j);
            }
        } else { //i!=0且j!=0;
            int p1 = process(str1,str2,i-1,j);
            int p2 = process(str1,str2,i,j-1);
            int p3 = str1[i] == str2[j] ? 1+process(str1,str2,i-1,j-1) : 0;
            return Math.max(p1,Math.max(p2,p3));
        }
    }

测试

public static void main(String[] args) {
   	System.out.println(longestCommonSubsequence("as123d24","dsc12t32h4"));
}

输出
6

动态规划

在递归的过程中,i和j是全过程中不断变化的量,我们可以用一个二维数组将每一个i/j对应的值记录下来。

i的取值范围:0~str1.length()-1;

j的取值范围:0~str2.length()-1;

对二维数组的赋值方法类比上述暴力递归过程:

public static int longestCommonSubsequence1 (String str1, String str2) {
        if (str1==null || str2==null || str1.length()==0 || str2.length()==0) {
            return 0;
        }
        int N = str1.length();
        int M = str2.length();
        int[][] dp = new int[N][M];
        char[] s1 = str1.toCharArray();
        char[] s2 = str2.toCharArray();
        //i==0且j==0;
    	dp[0][0] = s1[0] == s2[0] ? 1 : 0;
        //i==0且j!=0;
    	for (int j = 1; j < M; j++) {
            dp[0][j] = s1[0] == s2[j] ? 1 : process(s1,s2,0,j-1);
        }
    	//j==0且i!=0;
        for (int i = 1; i < N; i++) {
            dp[i][0] = s1[i] == s2[0] ? 1 : process(s1,s2,i-1,0);
        }
    	//j!=0且i!=0;
        for (int i = 1; i < N; i++) {
            for (int j = 1; j < M; j++) {
                int p1 = dp[i-1][j];
                int p2 = dp[i][j-1];
                int p3 = s1[i] == s2[j] ? 1 + dp[i-1][j-1] : 0;
                dp[i][j] = Math.max(p1,Math.max(p2,p3));
            }
        }
        return dp[N-1][M-1];
    }

测试

public static void main(String[] args) {
   System.out.println(longestCommonSubsequence("as123d24","dsc12t32h4"));      

System.out.println(longestCommonSubsequence1("as123d24","dsc12t132h4")) 
}
输出
6
6

最长回文子序列

最长回文子序列的解法有多种,这里拿出来是因为可以用上述最长公共子序列问题求解。

假定这里给定一个字符串:"a122egdfg3h42q1"

我们只需要算出这个字符串的逆字符串:“1q24h3gfdge221a”

然后对两个字符串求解公共最长子序列,即为最长回文子序列长度。

这里给一个字符串逆串求解思路

public static int longestMirrorSubsequence(String str) {
        if (str == null || str.length()==0) {
            return 0;
        }
        String mirror = "";
        char[] c = str.toCharArray();
        for (int i = c.length-1; i >=0; i--) {
            mirror += c[i];
        }
        return longestCommonSubsequence1(str,mirror);
    }

测试

 public static void main(String[] args) {
   System.out.println(longestCommonSubsequence("as123d24","dsc12t32h4"));    	

System.out.println(longestCommonSubsequence1("as123d24","dsc12t132h4"));
   System.out.println(longestMirrorSubsequence("a122egdfg3h42q1"));
 }

输出
6
6
7

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

superzheng__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值