代码随想录算法训练营day55||392.判断子序列||115.不同的子序列||583. 两个字符串的删除操作

392.判断子序列

题目描述:

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。

思路:

这道题我们可以统计,s和t的最长公共子序列,然后判断最长公共子序列的长度是否等于字符串s的长度,如果相等的话,return true,else return false;

动规五部曲:

1.dp[i][j]:到下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列长度为dp[i][j]。

注意这里是判断s是否为t的子序列。即t的长度是大于等于s的。

2.递推公式的确定:

在确定递推公式的时候,首先考虑下面两个操作:

if(s[i-1]==t[j-1]) //t中找到一个字符出现在s中也出现了

if(s[i-1]!=t[j-1]) //相当于t要删除元素,继续匹配

dp[i][j-1]+1;因为找到一个一个相同字符,相同子序列长度自然在dp[i-1][j-1]的基础上加1.

if(s[i-1]!=t[j-1]),此时相当于t要删除元素,t如果把当前元素t[j-1]删除,那么dp[i][j]的数值就是看s[i-1]与t[j-2]的比较结果,即:dp[i][j]=dp[i][j-1];

3.初始化:

递推公式可以看出dp[i][j]依赖于dp[i-1][j-1]和dp[i][j-1],所以一定要初始化dp[0][0]和dp[i][0].

4.遍历顺序从上到下,从左到右

class Solution {
public:
    bool isSubsequence(string s, string t) {
        vector> dp(s.size() + 1, vector(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];
            }
        }
        if (dp[s.size()][t.size()] == s.size()) return true;
        return false;
    }
};

115.不同的子序列

给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。

字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,"ACE" 是 "ABCDE" 的一个子序列,而 "AEC" 不是)

题目数据保证答案符合 32 位带符号整数范围。

思路:

这题和上一题有一些相似之处,只不过这一题不是判断一个字符串是否是另一个字符串的子序列,而是判断一个字符串可以通过删除元素得到几次另一个字符串。

动规五部曲:

1.dp[i][j]及其下标的定义:以i-1结尾的字符串s中出现以j-1结尾的字符串t的次数为dp[i][j]

2.递推公式:判断s[i-1]与t[j-1]如果相等,dp[i][j]就由两部分组成:

-可以是s[i-1]等于t[j-1]

-可以是s[i-2]等于tj-1]

dp[i][j]=dp[i-1][j-1]+dp[i-1][j]

如果不相等:dp[i][j]=dp[i-1][j]。为什么呢?因为如果s[i-2]之前与t[j-1]匹配的话,那么依然保持原状。

3.初始化:根据递推公式我们可知要初始化dp[i][0]和dp[0][j].严格根据定义进行初始化就可以。

dp[i][0]代表:以下标i-1结尾的字符串s中,任意删除元素,构成空字符串的个数,为1.

dp[0][j]代表:以空字符串s,任意删除元素,构成以j-1结尾的字符串的个数,为0.

4.遍历顺序:从上往下,从前往后

​
class Solution {
public:
    int numDistinct(string s, string t) {
        vector> dp(s.size() + 1, vector(t.size() + 1));
        for (int i = 0; i < s.size(); i++) dp[i][0] = 1;
        for (int j = 1; j < t.size(); j++) dp[0][j] = 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] + dp[i - 1][j];
                } else {
                    dp[i][j] = dp[i - 1][j];
                }
            }
        }
        return dp[s.size()][t.size()];
    }
};

​

583. 两个字符串的删除操作

思路:

方法1:求出两字符串的最长公共子序列,然后返回两字符串长度和-最长公共子序列*2

方法2:类似于上一题,上一题是删除一个字符串中元素,这一题是两个字符串中元素都需要删除,

动规五部曲:

1.dp[i][j]定义:以i-1结尾的字符串s和以j-1结尾的字符串t想要达到相等,一共要删除的元素数量

2.递推公式:两种情况:1.当判断s[i-1]==t[j-1]时,dp[i][j]=dp[i-1][j-1]

        2.如果s[i-1]和t[j-1]不同,则有三种情况:

-dp[i][j]=dp[i-1][j]+1   和 dp[i][j]=dp[i][j-1]+1   和    dp[i][j]=dp[i-1][j-1]+2

三者中取最小。

3.初始化:从递推公式上看dp[i][0]和dp[0][j]都必须初始化。

dp[i][0]=i  ;  dp[0][j]=j

4.遍历顺序:从上往下,从左到右。

class Solution {
public:
    int minDistance(string word1, string word2) {
        vector> dp(word1.size() + 1, vector(word2.size() + 1));
        for (int i = 0; i <= word1.size(); i++) dp[i][0] = i;
        for (int j = 0; j <= word2.size(); j++) dp[0][j] = j;
        for (int i = 1; i <= word1.size(); i++) {
            for (int j = 1; j <= word2.size(); 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] + 1, dp[i][j - 1] + 1);
                }
            }
        }
        return dp[word1.size()][word2.size()];
    }
};

你可能感兴趣的:(算法)