代码随想录算法训练营第五十二天| 300. 最长递增子序列、674. 最长连续递增序列、718. 最长重复子数组。

300. 最长递增子序列

题目链接:力扣

题目要求:

        给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

示例 1:

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
  • 1 <= nums.length <= 2500
  • -104 <= nums[i] <= 104

总结:

        定义dp数组含义,dp[i]代表以第i个元素结尾的的最长子序列长度。初始化,每个dp[i]至少都是1,全都先用1填满。递推公式,用i去遍历每个位置的元素,得出相应的dp[i],内层用j去遍历其前面的每个位置的元素,如果i的前面,j位置的元素小于i位置,说明符合递增,用j位置的dp[j]的状态,也就是求得的以j结尾的最长递增子序列长度,用这个dp再加1,得到这个情况的dp[i],每次遍历一个符合情况的j,求出dp[i],都要与之前情况的dp[i]比较,最终求出以i位置元素结尾的最大的递增子序列长度,最终返回的数组的最大递增子序列长度,不一定是非要以nums.length-1位置结尾元素求出的dp[nums.length-1],而是需要遍历整个dp,求出最大的那个值,返回。

class Solution {
    public int lengthOfLIS(int[] nums) {
        //dp[i]代表以第i个元素结尾的的最长子序列长度
        int[] dp = new int[nums.length];
        //每个dp[i]至少都是1
        Arrays.fill(dp,1);
        //用i去从前向后遍历每个元素求dp[i],用j去遍历i前的每个dp,只要i这个元素大于j位置元素,dp[i]=dp[j]+1,求出值最大的赋给最终的dp[i]。就可以找到以第i个元素结尾的最长子序列长度
        for(int i = 1;i < nums.length;i++){
            for(int j =0;j < i;j++){
                if(nums[i] > nums[j]){
                    dp[i] = Math.max(dp[i],dp[j]+1);
                }
            }
        }
        //最终最长的子序列不是返回dp[nums.length-1]的值,因为以最后一个元素结尾的最长子序列长度不一定是整个数组的最长子序列长度,需要遍历每个dp[i],找到长度最长的那个子序列返回。
        int result = 0;
        for(int i = 0;i < nums.length;i++){
            result = Math.max(result,dp[i]);
        }
        return result;
    }
}

674. 最长连续递增序列

题目链接:力扣

题目要求:

        给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。

连续递增的子序列 可以由两个下标 l 和 rl < r)确定,如果对于每个 l <= i < r,都有 nums[i] < nums[i + 1] ,那么子序列 [nums[l], nums[l + 1], ..., nums[r - 1], nums[r]] 就是连续递增子序列。

示例 1:

输入:nums = [1,3,5,4,7]
输出:3
解释:最长连续递增序列是 [1,3,5], 长度为3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为 5 和 7 在原数组里被 4 隔开。 
  • 1 <= nums.length <= 104
  • -109 <= nums[i] <= 109

总结:

        本题与上题的区别就在于它所求得的是连续的递增子序列,其他操作都不变,这里不需要再用一个内循环j去遍历i前的每个位置的元素了,而是直接与i前面一个元素比较,符合递增则,dp[i]=dp[i-1]+1,在遍历的同时,再记录dp数组中的最大值,最后返回即是最终需要求得的数组的最大的连续的递增子序列长度。

class Solution {
    public int findLengthOfLCIS(int[] nums) {
        int[] dp = new int[nums.length];
        Arrays.fill(dp,1);
        //记录最长连续递增序列长度
        int result = 1;
        for(int i = 1;i < nums.length;i++){
            //因为要求连续递增子序列,所以求第i个元素的dp时,只和其前一个位置的元素比较,大于前一个元素则dp[i] = 前一个元素的dp + 1;
            if(nums[i] > nums[i-1]){
                dp[i] = dp[i-1] + 1;
                result = Math.max(result,dp[i]);
            }
        }
        return result;
    }
}

718. 最长重复子数组

题目链接:力扣

题目要求:

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

示例 1:

输入:nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7]
输出:3
解释:长度最长的公共子数组是 [3,2,1] 。
  • 1 <= nums1.length, nums2.length <= 1000
  • 0 <= nums1[i], nums2[i] <= 100

总结:

        dp[i][j] 表示nums1,i-1位置结尾,nums2,j-1位置结尾的最长重复子数组长度,这里根据递推公式dp[0][0]的初始化的值需要为0才满足要求,dp[1][1]代表nums1位置为0,nums2位置为0,也就是两个数组都是第一个元素且相等时,dp[1][1]由dp[0][0]+1推出,故此时dp[0][0]应初始化为0,才符合这种情况下的dp值,也就是最长重复子数组为1。同理dp[i][0],dp[0][j]也都初始化是0,才都符合递推公式。这里的dp[i][j]都是由其两个数组位置的前一个的状态dp[i-1][j-1]+1推出来的,这里好好理解,与上题一致在遍历的时候记录最大的那个dp,用result记录。

class Solution {
    public int findLength(int[] nums1, int[] nums2) {
        //dp[i][j] 表示nums1,i-1位置结尾,nums2,j-1位置结尾的最长重复子数组长度
        int[][] dp = new int[nums1.length + 1][ nums2.length + 1];
        int result = 0;
        for(int i = 1;i < nums1.length + 1;i++){
            for(int j = 1;j < nums2.length + 1;j++){
                if(nums1[i-1] == nums2[j-1]){
                    //其最长重复子数组长度只能由ij各前一个的状态得出。
                    dp[i][j] = dp[i-1][j-1] + 1;
                    result = Math.max(result,dp[i][j]);
                }    
            }
        }
        return result;
    }
}

你可能感兴趣的:(leetcode,算法,java,动态规划,数据结构)