【超详细】动态规划子序列问题总结

子序列就是在一个数组中按顺序挑选若干数,子数组是连续的子序列,视为特殊的子序列问题

子序列

最长递增子序列

300. 最长递增子序列 - 力扣(LeetCode)https://leetcode.cn/problems/longest-increasing-subsequence/description/

  1. dp[i]表示以第i个数为结尾的,前i个数中最长严格递增子序列的长度。
  2. 递推得遍历之前所有的位置,并且满足第i个位置比第j个位置大,才能保证正确
  3. 初始化第0个位置为1
  4. 遍历顺序从0到size-1
  5. 举例略

代码实现

class Solution {
public:
    //dp[i]是以nums[i]结尾的最长严格递增子序列的长度
    int lengthOfLIS(vector& nums) {
        int n=nums.size();
        vector dp(n,1);
        int max1=1;
        for(int i=1;i nums[j])
                {
                    dp[i]=max(dp[i],dp[j]+1);
                    max1=max(max1,dp[i]);
                }
            }
        }

        return max1;
    }
};

最长连续递增序列(最长递增子数组)

674. 最长连续递增序列 - 力扣(LeetCode)674. 最长连续递增序列 - 给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。连续递增的子序列 可以由两个下标 l 和 r(l < 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 隔开。 示例 2:输入:nums = [2,2,2,2,2]输出:1解释:最长连续递增序列是 [2], 长度为1。 提示: * 1 <= nums.length <= 104 * -109 <= nums[i] <= 109https://leetcode.cn/problems/longest-continuous-increasing-subsequence/

  1. dp[i]的含义是以第i个数为结尾,前i个数中最长的递增子序列长度
  2. 做个判断,如果nums[i]>nums[i-1],则dp[i]=dp[i-1]+1
  3. 全都初始化为1
  4. 遍历从0到nums.size()-1
  5. 举例略

代码实现

class Solution {
public:
    //dp[j]  表示以j结尾的连续递增子序列长度
    //递推公式
    int findLengthOfLCIS(vector& nums) {
        int n=nums.size();
        int maxAns=1;
        vector dp(n,1);
        for(int i=1;inums[i-1]) {
                dp[i]=dp[i-1]+1;
                if(dp[i]>maxAns) maxAns=dp[i];
            }
        }
        return maxAns;
    }
};

附一个双指针实现

class Solution {
public:
    //dp[j]  表示以j结尾的连续递增子序列长度
    //递推公式
    int findLengthOfLCIS(vector& nums) {
        int n=nums.size();
        int l,r=1;
        int maxlen=1;
        for(l=0;lnums[r-1])
            {   
                maxlen=max(maxlen,r-l+1);
                r++;
            }
            //这个重置状态你得想一下
            l=r-1;r=r+1;
        }
        return maxlen;
    }
};

最长公共子数组

718. 最长重复子数组 - 力扣(LeetCode)https://leetcode.cn/problems/maximum-length-of-repeated-subarray/description/

  1. dp[i][j] 表示nums1中以i结尾,nums2中以j结尾的最长子数组长度
  2. 递推的时候如果nums1[i]=nums2[j]   dp[i][j]=dp[i-1][j-1]+1,否则dp[i][j]=0;
  3. 初始化都等于0
  4. 遍历的时候都从0到size
  5. 举例略

代码实现

class Solution {
public:
    int findLength(vector& nums1, vector& nums2) {
        //子数组是连续子序列
        int sizeA=nums1.size();
        int sizeB=nums2.size();
        int max=0;
        //dp[i][j]的含义是nums1第i个数结尾的子数组和nums2第j个数结束的子数组公共的最大长度
        vector> dp(sizeA+1,vector(sizeB+1,0));
        for(int i=1;i<=sizeA;i++)
        {
            for(int j=1;j<=sizeB;j++)
            {
                if(nums1[i-1]==nums2[j-1])
                {
                    dp[i][j]=dp[i-1][j-1]+1;
                    if(dp[i][j]>=max)
                    {
                        max=dp[i][j];
                    }
                }
                
            }
        }
        return max;
    }
};

最长公共子序列

1143. 最长公共子序列 - 力扣(LeetCode)https://leetcode.cn/problems/longest-common-subsequence/description/

  1. dp[i][j]表示nums1以第i个数结尾,nums2以第j个数结尾的最长公共子序列长度
  2. 递推公式,如果第i个数字和第j个数字想等,dp[i][j]=dp[i-1][j-1]+1;如果不相等,则dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
  3. 初始化,都初始化为0即可
  4. 遍历,从0到size
  5. 举例略

代码实现

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        //dp[i][j]表示以text1以i-1结尾,text2以j-1结尾的公共子序列最长长度
        int size1=text1.size();
        int size2=text2.size();

        vector>dp(size1+1,vector(size2+1,0));
        for(int i=1;i<=size1;i++)
        {
            for(int j=1;j<=size2;j++)
            {   
                
                if(text1[i-1]==text2[j-1])
                {   
                    dp[i][j]=dp[i-1][j-1]+1;
                }
                else
                {
                    dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
                }
            }

        }

        return dp[size1][size2];
    }
};

不相交的线

1035. 不相交的线 - 力扣(LeetCode)https://leetcode.cn/problems/uncrossed-lines/description/这题实际上就是求一个最长公共子序列

代码实现

class Solution {
public:
    int maxUncrossedLines(vector& nums1, vector& nums2) {
        int size1=nums1.size(),size2=nums2.size();
        vector>dp(size1+1,vector(size2+1,0));
        for(int i=1;i<=size1;i++)
        {
            for(int j=1;j<=size2;j++)
            {
                if(nums1[i-1]==nums2[j-1])
                {
                    dp[i][j]=dp[i-1][j-1]+1;
                }
                else
                {
                    dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }
        return dp[size1][size2];
    }
};

最大子数组和 

53. 最大子数组和 - 力扣(LeetCode)https://leetcode.cn/problems/maximum-subarray/

  1. dp[i]表示以i结尾的最大子数组和
  2. 递推公式,如果dp[i-1]>0,则dp[i]=dp[i-1]+nums[i];否则自己开山,dp[i]=nums[i]
  3. 初始化dp[0]=num[0];
  4. 递推从0到n
  5. 举例略

代码实现

class Solution {
public:
    int maxSubArray(vector& nums) {
        if (nums.size() == 0) return 0;
        vector dp(nums.size());
        dp[0] = nums[0];
        int result = dp[0];
        for (int i = 1; i < nums.size(); i++) {
            dp[i] = max(dp[i - 1] + nums[i], nums[i]); // 状态转移公式
            if (dp[i] > result) result = dp[i]; // result 保存dp[i]的最大值
        }
        return result;
    }
};

判断子序列

392. 判断子序列 - 力扣(LeetCode)https://leetcode.cn/problems/is-subsequence/description/

双指针解法

class Solution {
public:
    bool isSubsequence(string s, string t) {
        int n = s.length(), m = t.length();
        int i = 0, j = 0;
        while (i < n && j < m) {
            if (s[i] == t[j]) {
                i++;
            }
            j++;
        }
        return i == n;
    }
};

动态规划解法

判断最长公共子序列长度和t的大小是否相等

class Solution {
public:
    bool isSubsequence(string s, string t) {
        //实际上就是最长公共子序列,如果这个最长公共子序列的长度是s的长度,则是
        int n = s.length(), m = t.length();
        //dp[i][j] 表示以j结尾的子数组 是以i结尾的字串,相同子序列的长度
        vector> dp(n+1,vector(m+1,false));
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                if(s[i-1]==t[j-1])
                {
                    dp[i][j]=dp[i-1][j-1]+1;
                }
                else
                {
                    dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }

        //最长公共子序列dp[i-1][j],dp[i][j-1]
        
        return dp[n][m]==s.size();
    }
};

使用编辑的思想去做

class Solution {
public:
    bool isSubsequence(string s, string t) {
        int len1=s.size();
        int len2=t.size();
        vector> dp(len1+1,vector(len2+1));
        
        //s是待匹配,j是大的
        //dp[i][j]的含义是s以i结尾的字符串是不是t以j结尾的字符串的字串
        for(int j=0;j<=len2;j++)
        {
            dp[0][j]=1;
        }
        for(int i=1;i<=len1;i++)
        {
            for(int j=1;j<=len2;j++)
            {
                if(s[i-1]==t[j-1])
                {
                    dp[i][j]=dp[i-1][j-1]||dp[i][j-1];
                }
                else
                {
                    dp[i][j]=dp[i][j-1];
                }
            }
        }
        return dp[len1][len2];
    }
};

两个字符串的删除操作

583. 两个字符串的删除操作 - 力扣(LeetCode)https://leetcode.cn/problems/delete-operation-for-two-strings/

代码实现

class Solution {
public:
    int minDistance(string word1, string word2) 
    {
        //dp[i][j]表示word1以i结尾和word2以j结尾的最小步数
        int len1=word1.size(),len2=word2.size();
        vector> dp(len1+1,vector(len2+1));
        for(int i=0;i<=len1;i++)
        {
            dp[i][0]=i;
        }
        for(int j=0;j<=len2;j++)
        {
            dp[0][j]=j;
        }
        for(int i=1;i<=len1;i++)
        {
            for(int j=1;j<=len2;j++)
            {
                if(word1[i-1]==word2[j-1])
                {
                    dp[i][j]=dp[i-1][j-1];
                }
                else
                {
                    dp[i][j]=min(dp[i-1][j],dp[i][j-1])+1;
                }
            }
        }
        return dp[len1][len2];
    }
};

编辑距离

72. 编辑距离 - 力扣(LeetCode)https://leetcode.cn/problems/edit-distance/

代码实现

class Solution {
public:
    int minDistance(string word1, string word2) {
        //dp[i][j]的含义是把word1以i结尾的子串转化成word2以j结尾的字串的最少操作数
        int len1=word1.size();
        int len2=word2.size();
        vector>dp(len1+1,vector(len2+1,0));
        for(int i=0;i<=len1;i++)
        {
            dp[i][0]=i;
        }
        for(int j=0;j<=len2;j++)
        {
            dp[0][j]=j;
        }

        for(int i=1;i<=len1;i++)
        {
            for(int j=1;j<=len2;j++)
            {
                if(word1[i-1]==word2[j-1])
                {
                    dp[i][j]=dp[i-1][j-1];
                }
                else
                {
                    dp[i][j]=min(dp[i-1][j],dp[i][j-1]);
                    dp[i][j]=min(dp[i][j],dp[i-1][j-1]);
                    dp[i][j]+=1;
                }
            }
        }    
        return dp[len1][len2];  
          
        }
};

你可能感兴趣的:(动态规划,算法)