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

392.判断子序列、115.不同的子序列

392.判断子序列

一般解法

对于该题,我们的一般解法,就是遍历一次s数组,如果可以在t数组中顺序找到这些元素,那么我们可以判断s是t的自序列。因此,我们可以用两层循环,并且用k来记录我们遍历到t数组的位置,防止重复遍历。
因此,我们可以写出以下代码。

代码

class Solution {
public:
    bool isSubsequence(string s, string t) {
        int k = 0;
        int u = 0;
        if(s.size()>t.size()) return false;
        for(int ii = 0;ii<s.size();ii++)
        {
                for(int jj = k;jj<t.size();jj++)
                {
                    if(s[ii] == t[jj])
                    {
                        k = jj+1;
                        u++;
                        break;
                    }
                }
        }
        if(u==s.size())return true;
        return false;

    }
};

动态规划

我们也可以用动态规划的思路去解决该问题,如同之前处理最长字符串的思路,我们也可以用dp数组来记录已经记录的公共子字符串长度。如果dp数组最后行最后列的元素记录为s数组的大小,说明s是t的子字符串。
动态规划四部曲。
1.dp数组的含义。
dp[ii][jj]表示数组s的下标为0-ii-1与数组t的下标为0-jj-1所能记载的最长公共自序列。这是和处理最长公共子序列的思路一致。
2.dp数组的递推公式。

if(s[ii-1]==t[jj-1]) dp[ii][jj] = dp[ii-1][jj-1]+1;
                else dp[ii][jj] = max(dp[ii-1][jj],dp[ii][jj-1]);

3.dp数组的初始化。我们令所有第一行和第一列的元素全为0,表示,下标0到下标-1的元素最长公共子序列长度为0,便于进行元素的递推。
4.先遍历哪一个数组都可以,这个没有必要在意。

代码

class Solution {
public:
    bool isSubsequence(string s, string t) {
        vector<vector<int>>dp(s.size()+1,vector<int>(t.size()+1,0));
        for(int ii =1;ii<s.size()+1;ii++)
        {
            for(int jj =1;jj<t.size()+1;jj++)
            {
                if(s[ii-1]==t[jj-1]) dp[ii][jj] = dp[ii-1][jj-1]+1;
                else dp[ii][jj] = max(dp[ii-1][jj],dp[ii][jj-1]);
            }
        }
        if(dp[s.size()][t.size()]==s.size()) return true;
        else return false;
    }
};

115.不同的子序列

这道题我们利用动态规划进行解决。
与其他子序列不同的是,这道题要求的是不同子序列的数量,不单单是判断是否是子序列以及子序列长度,因此用以往的思路进行解决已经无法满足于此问题。

首先,对于字符串中任意两个元素,他们都有相同与不相同两种情况,因此,我们需要进行不同的判断。
我们用dp[ii][jj]表示以元素s[ii-1]t[jj-1]为结尾的子序列种类。

如果遍历的两个字符串中的元素s[ii-1]与t[jj-1]相同,那么我们对dp[ii][jj]进行更新,我们需要分两种情况,使用s[ii-1]与不使用s[ii-1]。
如果使用s[ii-1],那么截止t[jj-1]其子序列数就应该是dp[ii-1][jj-1],因为s[ii-1]与t[jj-1]相互匹配的话,那么dp[ii-1][jj-1]就是两个序列的前一个元素的子序列数。
如果不使用s[ii-1],那么截止t[jj-1]其子序列数就应该是dp[ii-1][jj]。这是dp数组的定义。
因此如果两个字符串中的元素s[ii-1]与t[jj-1]相同,那么我们应该更新dp[ii][jj]为dp[ii][jj] = dp[ii-1][jj-1]+dp[ii-1][jj];

如果不相同,那么,我们继承不使用s[ii-1]的情况即可。dp[ii][jj] = dp[ii-1][jj];

初始化,对于数组的初始化,我们需要对第一列和第一行进行初始化。
根据dp数组的定义,如果t为空,空字符串应该是任何字符串的子集,所以令
dp[ii][0]=0
但是如果s为空,任何字符串(非空)不是空字符串的子集,因此令
dp[0][jj] = 0

动态规划四部曲。
1.dp数组的含义。dp[ii][jj]表示以元素s[ii-1]t[jj-1]为结尾的子序列种类。
2.dp数组的推导公式。

if(s[ii-1]==t[jj-1]) dp[ii][jj] = dp[ii-1][jj-1]+dp[ii-1][jj];
else dp[ii][jj] = dp[ii-1][jj];

3.dp数组的初始化。

dp[ii][0] = 1;//其余为0

4.遍历顺序,我们可以先访问s数组后访问t数组。

代码

class Solution {
public:
    int numDistinct(string s, string t) {
        if(s.size()<t.size())
        return 0;
        vector<vector<long long unsigned int>>dp(s.size()+1,vector<long long unsigned int>(t.size()+1,0));
        for(int ii = 0;ii<s.size()+1;ii++)
        dp[ii][0] = 1;
        for(int ii = 1;ii<s.size()+1;ii++)
        {
            for(int jj =1;jj<t.size()+1;jj++)
            {
                if(s[ii-1]==t[jj-1]) dp[ii][jj] = dp[ii-1][jj-1]+dp[ii-1][jj];
                else dp[ii][jj] = dp[ii-1][jj];
            }
        }
        return dp[s.size()][t.size()];
    }
};

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