【刷题笔记】--dp--子序列问题

目录

题目1:最长递增子序列

题目2:最长公共子序列


题目1:最长递增子序列


思路: 

 如果是用动态规划的话,我们就要先清楚dp[i]表示啥,里面的i表示啥。首先这道题的 i 表示给的数组nums的各个元素的下标。dp[i]表示从数组头到nums[i]这段序列的最长递增子序列的元素个数。

得到所有nums[i]的dp[i]后,最后取这些dp[i]的最大值就是我们的最长递增子序列的元素个数。

所以我们的目标就是去求每个nums[i]的dp[i]值。

怎么去求dp[i]?就要我们用到动态规划的思路。

dp[i]怎么用更小的子问题去表示呢?我们用j去遍历比i小的所有元素,如果这个元素即nums[ j1 ]小于nums[i]这个元素,说明nums[i]可以加入到dp[ j1 ]这个序列,加入后,①dp[i]=dp[ j1 ]+1;在遍历的时候,肯定会不止一个元素小于nums[i],记作nums[ j2 ],计算②dp[i]=dp[ j2 ]+1;我们看①②哪个得到的dp[i]大,即我们最后保留的dp[i]值。

代码:

class Solution {
    public int lengthOfLIS(int[] nums) {
        int[] dp=new int[nums.length];
        Arrays.fill(dp,1);
        for(int i=0;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);
                }
            }
        }
        int res=0;
        for(int i=0;i<nums.length;i++){
            if(dp[i]>res){
                res=dp[i];
            }
        }
        return res;
    }
}

题目1.1 53. 最大子数组和 

思路: dp[i]表示 数组头到下标为i的数组元素这一段数组序列的最大连续子数组的值,dp的思想就是用更小规模的问题去表示原问题。

所以我们就会想到dp[i]=dp[i-1]+nums[i]。因为要考虑到是要最大的,所以我们计算dp[i]时,如果加后的值比加的这个元素本身还小,那还不如不加。所以 dp[i]=max(dp[i-1]+nums[i],nums[i])。

最后取这些元素对应的dp[i]最大值即可。

 代码:

class Solution {
    public int maxSubArray(int[] nums) {
        int[] dp=new int[nums.length];
        dp[0]=nums[0];
        for(int i=1;i<nums.length;i++){
            dp[i]=Math.max(dp[i-1]+nums[i],nums[i]);
        }
        int res=dp[0];
        for(int i=0;i<nums.length;i++){
            if(dp[i]>res){
                res=dp[i];
            }
        }
        return res;
    }
}

题目1.2:674. 最长连续递增序列 

这道题简单,直接上代码:

class Solution {
    public int findLengthOfLCIS(int[] nums) {
        int[] dp=new int[nums.length+1];
        dp[0]=1;
        for(int i=1;i<nums.length;i++){
            if(nums[i]>nums[i-1]){
                dp[i]=dp[i-1]+1;
            }
            else{
                dp[i]=1;
            }
        }
        int res=0;
        for(int i=0;i<nums.length;i++){
            if(dp[i]>res){
                res=dp[i];
            }
        }
        return res;
    }
}

 题目2:最长公共子序列


思路:

我们拿

text1 = "abcde", text2 = "ace"举例:

我们定义dp[i][j],i表示指向text1字符串的字符下标。j表示指向text2字符串的字符下标。

dp[i][j]表示字符串①和②最长公共子序列的元素个数。

那我们现在要怎么表示这个dp[i][j]的值呢?我们肯定是要用更小的规模去表示,即dp[i-1][j-1],dp[i-1][j],dp[i][j-1](就是更小规模的两个字符串序列)等来表示。 

但是i,j 所指的这个字符会有两种情况,一种是这两个字符相同:

 这个时候dp[i][j]就等于dp[i-1][j-1]+1。(按照情境来解释就是①②两个更小规模的字符串的最长公共子序列的元素个数+我们现在扫描到的相同元素,就是我们要求的原问题。)

一种是这两个字符不相同:

 

这个时候dp[i][j]=max(dp[i-1][j],dp[i][j-1])。 (按照情境来解释就是dp[i][j]可以用紫色框的两个更小规模的字符串的最大公共子序列来表示or可以用红色框的两个更小规模的字符串的最大公共子序列来表示)

按上面的方法分析,就可以依次画出每个dp[i][j]的值:

 代码:

class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        int[][] dp=new int[text1.length()+1][text2.length()+1];
        for(int i=1;i<=text1.length();i++){
            char char1=text1.charAt(i-1);
            for(int j=1;j<=text2.length();j++){
                char char2=text2.charAt(j-1);
                if(char1==char2){
                    dp[i][j]=dp[i-1][j-1]+1;
                }
                else{
                    dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }
        return dp[text1.length()][text2.length()];
    }
}

题目2.1:1035. 不相交的线

思路:因为要满足每条线不相交,可以理解为每个相等的数的相对位置不变,所以相当于就是问最长公共不连续序列的元素个数。

代码(与上一道题目一样):

class Solution {
    public int maxUncrossedLines(int[] nums1, int[] nums2) {
        int[][] dp=new int[nums1.length+1][nums2.length+1];
        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]);
                }
            }
        }
        return dp[nums1.length][nums2.length];
    }
}

题目2.2:718. 最长重复子数组

思路:也是一样按照上面的题去想,首先先确定dp[i][j]是指在前i个nums1元素和前j个nums2元素里最长公共子数组的元素个数。

所以当i所指的元素与j所指的元素相同时,dp[i][j]=dp[i-1][j-1]+1.

而如果当i所指的元素与j所指的元素不相同,则不作操作。

class Solution {
    public int findLength(int[] nums1, int[] nums2) {
        int[][] dp=new int[nums1.length+1][nums2.length+1];
        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;
                    res=Math.max(dp[i][j],res);//每次都保留最大的
                }
            }
        }
        return res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值