代码随想录算法训练营第五十五天|392.判断子序列、115.不同的子序列

LeetCode 392 判断子序列

题目链接:https://leetcode.cn/problems/is-subsequence/

思路:

  • dp数组的含义
    dp[i][j]代表以i-1结尾的s子串和以j-1结尾的t子串,相同子序列的长度

  • 递推公式
    本题有两种情况:
    1、s[i - 1] == t[j - 1]
    所以此时递推公式为:
    d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + 1 dp[i][j] = dp[i-1][j-1]+1 dp[i][j]=dp[i1][j1]+1
    2、text1[i - 1] != text2[j - 1]
    例子:s:abc t:abcde
    本题是判断s是否为t的子串,所以t的长度必然大于等于s,所以只能在t子串上做删减操作
    所以此时递推公式是:
    d p [ i ] [ j ] = d p [ i ] [ j − 1 ] dp[i][j] = dp[i][j-1] dp[i][j]=dp[i][j1]

  • 初始化
    dp[i][0]和dp[0][j]显然都是没有意义的,即二维数组的第一行和第一列,将其全部初始化为0即可。其余数值因为会在递推公式中被覆盖,所以也都初始化为0,这样可以使得代码相对简洁。

  • 遍历顺序
    显然遍历是从上到下,从左到右

代码:

class Solution {
public:
    bool isSubsequence(string s, string t) {
        vector<vector<int>>dp(s.size() + 1, vector<int>(t.size() + 1, 0));

        for(int i = 1; i <= s.size(); i++)
        {
            for(int j = 1; j <= t.size(); j++)
            {
                if(s[i - 1] == t[j - 1])
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                else
                    dp[i][j] = dp[i][j - 1];
            }
        }

        return dp[s.size()][t.size()] == s.size() ? true : false;
    }
};

总结

类似最大公共子序和,区别在于本题只需要在t字符串上做删除操作


LeetCode 115 不同的子序列

题目链接:https://leetcode.cn/problems/distinct-subsequences/

思路:

  • dp数组的含义
    dp[i][j]代表以i-1结尾的s子串出现以j-1结尾的t子串的个数

  • 递推公式
    本题有两种情况:
    s[i - 1] == t[j - 1]
    此时有两种选择:
    1、选择将s字符串中i-1位置的字符和t字符串中j-1位置的字符匹配,那么此时
    d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] dp[i][j] = dp[i-1][j-1] dp[i][j]=dp[i1][j1]因为s上第i-1的位置已经匹配上t上的j-1的位置,那么此时就要查看s中以i-2结尾的子串出现以j-2结尾的子串的个数。
    2、不选择将字符串s中i-1位置的字符和t字符串中j-1位置的字符匹配,即此时要考虑以i-2结尾的s子串出现以j-1结尾的t子串的个数。
    例子:s:bagg t:bag
    i = 4 i=4 i=4, j = 3 j=3 j=3,此时 s [ i − 1 ] = g s[i-1]=g s[i1]=g t [ j − 1 ] = g t[j-1]=g t[j1]=g不选择匹配的话即在bag(s[0:i-2])中找有包含bag(t[0:i-1])子串有多少个
    所以此时递推公式为:
    d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j] = dp[i-1][j] dp[i][j]=dp[i1][j]
    所以整体递推公式为:
    d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + d p [ i − 1 ] [ j ] dp[i][j] =dp[i-1][j-1]+dp[i-1][j] dp[i][j]=dp[i1][j1]+dp[i1][j]
    s[i - 1] != t[j - 1]
    本题是判断s包含多少个t,所以s的长度必然大于等于t,所以只能在s子串上做删减操作
    所以此时递推公式是:
    d p [ i ] [ j ] = d p [ i − 1 ] [ j ] dp[i][j] = dp[i-1][j] dp[i][j]=dp[i1][j]

  • 初始化
    d p [ i ] [ 0 ] dp[i][0] dp[i][0]代表s为正常子串,t为空字串的情况,显然, d p [ i ] [ 0 ] = 1 dp[i][0]=1 dp[i][0]=1
    d p [ 0 ] [ j ] dp[0][j] dp[0][j]代表s为空字串,在空字串上要包含有正常子串,显然不可能。所以 d p [ 0 ] [ j ] = 0 dp[0][j]=0 dp[0][j]=0

  • 遍历顺序
    显然遍历是从上到下,从左到右

代码:

class Solution {
public:
    int numDistinct(string s, string t) {
        vector<vector<uint64_t>>dp(s.size() + 1, vector<uint64_t>(t.size() + 1, 0));
        for(int i = 0; i <= s.size(); i ++)
        {
            dp[i][0] = 1;
        }

        for(int i = 1; i <= s.size(); i++)        
        {
            for(int j = 1; j <= t.size(); j++)
            {
                if(s[i - 1] == t[j - 1])
                {
                    dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j];
                }
                else
                dp[i][j] = dp[i - 1][j];
            }
        }

        return dp[s.size()][t.size()];
    }
};

总结

重点要理解 当 s [ i − 1 ] = = t [ j − 1 ] 当s[i-1]==t[j-1] s[i1]==t[j1]的时候要有两种情况,要把两种情况都考虑进去。


今日总结:

开始学习编辑距离的问题。

你可能感兴趣的:(代码随想录算法训练营,算法,leetcode,c++)