代码随想录-Day51~53-补动态规划-字序列问题-718. 最长重复子数组||1143. 最长公共子序列||1035. 不相交的线||53. 最大子数组和

-718. 最长重复子数组-

给两个整数数组 nums1 和 nums2 ,返回 两个数组中 公共的 、长度最长的子数组的长度 

示例 1:

输入:nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7]
输出:3
解释:长度最长的公共子数组是 [3,2,1] 。

解题思路:使用二位dp数组分别表示两个数组,dp[i][j]代表以第i-1和j-1结尾的字符数组的最长公共子序列的长度为dp[i][j],明白了这个,推导dp[i][j]则需要数组A【i-1】等于数组B【j-1】的时候对dp数组进行加1操作,这样推倒的dp数组内部就存放的是最长重复子数组的长度吗,注意是连续的

class Solution {
    public int findLength(int[] nums1, int[] nums2) {
        //动态规划五部曲
        //1,确定dp数组含义
        //dp[i][j],定义为以下标i-1为结尾的A和以下标j-1为结尾的B
        //最长重复子数组长度为dp[i][j]
        //为什么定义为i-1和j-1是为了后续处理的时候方便
        int[][] dp=new int[nums1.length+1][nums2.length+1];
        int result=0;
        //2, 确定递推公式
        //只有当A[i-1]和B[j-1]相等的时候
        //dp[i-1][j-1]才可以推导出dp[i][j]
        //dp[i][j]=dp[i-1][j-1]+1

        //3,dp数组初始化
        //因为dp[i][j]表示的是以i-1和j-1结尾的最长重复子数组的长度,所以
        //当i和j为0时无意义,直接初始化巍峨0即可,得益于最初的设定为i-1和j-1

        //4,循环遍历
        for(int i=1;i<=nums1.length;i++){
            for(int j=1;j<=nums2.length;j++){
                if(nums1[i-1]==nums2[j-1]){
                    dp[i][j]=dp[i-1][j-1]+1;
                    result=Math.max(dp[i][j],result);
                }
            }
        }

        return result;

    }
}

1143. 最长公共子序列

给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

  • 例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。

两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

解题思路:这道题和上一道的差别在于最长公共子序列可以是不连续的,只要位置正确就可以,所以需要在原本递推公式的基础之上在额外对两个字符不相等的情况进行判断,在这种情况之下,得出dp[i-1][j]和dp[i\]j-1]中的最大值即可。

class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        //其实和上一道题目差不多,但是这道题目求的是子序列,不是连续的子序列
        //所以定义的地推公式的含义是需要进行改变的
        int len1=text1.length();
        int len2=text2.length();
        int res=0;
        int[][] dp=new int[len1+1][len2+1];
        //dp数组的含义还是第text的i-1个字符和text2的j-1个字符的最长公共子序列
        //为dp[i][j]
        for(int i=1;i<=len1;i++){
            for(int j=1;j<=len2;j++){
                if(text1.charAt(i-1)==text2.charAt(j-1)){
                    //两个字符串最后一位字符相等,所以公共子序列长度加一
                    dp[i][j]=dp[i-1][j-1]+1;
                }else{
                    //如果两个字符最后一位不相等,说明公共子序列长度
                    //有可能是不连续的,例如
                    //ace   abcde
                    //子序列是 ace中e之前的,也可能相反
                    dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);
                }
                res=Math.max(dp[i][j],res);
            }
        }
        return res;
    }
}

1035. 不相交的线

在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。

现在,可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线,这些直线需要同时满足满足:

  •  nums1[i] == nums2[j]
  • 且绘制的直线不与任何其他连线(非水平线)相交。

请注意,连线即使在端点也不能相交:每个数字只能属于一条连线。

以这种方法绘制线条,并返回可以绘制的最大连线数。

解题思路:其实这道题的本质就是最长的子序列,只不过是变了个说法而已,所以求解的最长公共子序列就是直线条数。

class Solution {
    public int maxUncrossedLines(int[] nums1, int[] nums2) {
        //对于这道题目来说,其本质还是求解的公共子序列
        int[][] dp=new int[nums1.length+1][nums2.length+1];
        //初始化的时候第一行和第一列全部是0
        int res=0;
        for(int i=1;i<=nums1.length;i++){
            for(int j=1;j<=nums2.length;j++){
                if(nums1[i-1]==nums2[j-1]){
                    dp[i][j]=dp[i-1][j-1]+1;
                }else{
                    dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);
                }
                res=Math.max(dp[i][j],res);
            }
        }
        return res;
    }
}

53. 最大子数组和

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

解题思路1:贪心算法,贪心的思路就是从头开始累加,一旦累加一个数字之后当前子数组的和小于0,清空当前子数组和,重新累加,最后总比较出累加得到的最大值就是子数组的最大长度

贪心代码:

//使用贪心算法
class Solution {
    public int maxSubArray(int[] nums) {
       //如何求解局部最优解
       //从第一个开始累加结果,当遇到累加的数字导致前面的和小于0,说明
       //这个值就不能取,摒弃之后开始取下一个局部最优解
       int count=0;
       int result=Integer.MIN_VALUE;
       for(int i=0;i<nums.length;i++){
           count+=nums[i];
           //判断当前是否是局部最优
           if(count>result) result=count;
           //如果当前值导致和小于0,直接将结果集清空,找寻下一个最优解。
           if(count<0) count=0;
       }
       return result;
    }
}

解题思路2:动态规划,dp数组的含义 以第i个数字结尾的最大子数组的和为dp[i],dp[i]的状态可以有两种状态推导出来,一种是保持当前状态,不进行累计,另一种是在当前子数组的基础之上累加当前数组,两个状态求解最大值即可

动态规划代码:

class Solution {
    public int maxSubArray(int[] nums) {
        //使用贪心算法解决过
        //再使用动态规划解决
        //dp数组含义
        //dp[i]表示以i结尾的最大连续子序列和为dp[i]
        int[] dp=new int[nums.length];
        //dp数组初始化应该是dp[0]=nums[0]
        dp[0]=nums[0];
        int res=dp[0];
        for(int i=1;i<nums.length;i++){
            dp[i]=Math.max(nums[i],dp[i-1]+nums[i]);
            res=Math.max(dp[i],res);
        }
        return res;
    }
}

392. 判断子序列

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace""abcde"的一个子序列,而"aec"不是)。

进阶:

如果有大量输入的 S,称作 S1, S2, ... , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?

解题思路:判断子序列的思路其实就是求解两个字符的最长连续公共子序列,如果子序列的长度等于长度较小的字符说明是子序列,繁殖既不是

class Solution {
    public boolean isSubsequence(String s, String t) {
        //使用动态规划解决
        //整体的思路是求出两个字符串的最长子序列,如果最长子序列和最小的字符串
        //长度相等,那么就是子序列
        //其实这样求解有点过于复杂
        int len1=s.length();//3
        int len2=t.length();//0
        int[][] dp=new int[len1+1][len2+1];
        int res=0;
        for(int i=1;i<=len1;i++){
            for(int j=1;j<=len2;j++){
                if(s.charAt(i-1)==t.charAt(j-1)){
                    dp[i][j]=dp[i-1][j-1]+1;
                }else{
                    dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);
                }
                res=Math.max(dp[i][j],res);
            }
        }
        if(len2==0&&len1!=0) return false; 
        return len1<len2||len1==len2? len1==res:len2==res;
        
    }
}

                                                                                                                                By-三条直线围墙

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值