算法练习day47

41 阅读2分钟

一、判断子序列

字符串的子序列是原始字符串删除一些(或不删除)字符,不改变剩余字符相对位置的新字符串

五部曲

  1. 确定dp数组的含义,dp[i][j],代表下标i-1j结尾的字符串s,和下标j-1结尾的字符串t,相同子序列的长度
  2. 确定递推公式
if(s[i-1] === t[j-1]) {
    dp[i][j] = dp[i-1][j-1] + 1
} else {
    dp[i][j] = dp[i][j-1] // 相当于看s[i-1]和t[j-2]的比较结果
}
  1. dp数组初始化,dp[0][0]和dp[i][0]都初始化为0
  2. 遍历顺序为从上到下,从左到右
  3. 举例推导

时间复杂度:O(n × m) 空间复杂度:O(n × m)

/**
 * @param {string} s
 * @param {string} t
 * @return {boolean}
 */
var isSubsequence = function(s, t) {
    let dp = new Array(s.length + 1).fill(0).map(_ => new Array(t.length + 1).fill(0))
    for(let i = 1; i <= s.length;i++) {
        for(let j= 1;j <= t.length;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.length][t.length] === s.length
};

二、不同的子序列

字符串s和t,在s的子序列中t出现的个数

五部曲

  1. 确定dp数组的含义,dp[i][j],以i-1结尾的s子序列中出现以j-1结尾的t的个数
  2. 递推公式
if(s[i-1] === t[j-1]) {
    // 用s[i-1],或者不用,两种情况都可能匹配
    dp[i][j] = dp[i-1][j-1] + dp[i-1][j]
} else {
    dp[i][j] = dp[i-1][j]
}
  1. dp数组的初始化,dp[i][j]是从上方和左上方推导而来,dp[i][0]和dp[0][j]需要初始化
dp[i][0] = 1 // 以i-1结尾的字符串,出现空字符串的个数
dp[0][j] = 0
dp[0][0] = 1 // 空字符串,出现空字符串的个数为1
  1. 遍历顺序,左上方和正上方
  2. 举例推导dp
/**
 * @param {string} s
 * @param {string} t
 * @return {number}
 */
var numDistinct = function(s, t) {
    let dp = new Array(s.length + 1).fill(0).map(_ => new Array(t.length + 1).fill(0))
    for(let i = 0; i <= s.length; i++) {
        dp[i][0] = 1
    }
    for(let i = 1; i <= s.length;i++) {
        for(let j = 1; j <= t.length; 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.length][t.length]
};