代码随想录day47|392判断子序列115不同的子序列|01笔记

112 阅读4分钟
  • 392判断子序列

  • 代码随想录 (programmercarl.com)
  • 第一印象

  • 这题可以直接使用 最长公共子序列的方法。当得到的最长值达到子序列的长度,则代表满足子序列关系。
  • 也可以使用指针的方法。使用idx记录t的位置直接搜索相同的字符,如果能够满足搜索到s串最后,则成立。
  • 讲解观后感

  • 依旧是按照动态规划的固定解题路线。首先,dp[i][j] 表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]
  • 然后确定确定递推公式
    if (s[i - 1] == t[j - 1]),那么dp[i][j] = dp[i - 1][j - 1] + 1
    if (s[i - 1] != t[j - 1]),那么dp[i][j] 的数值就是 看s[i - 1]与 t[j - 2]的比较结果了,即:dp[i][j] = dp[i][j - 1]
  • dp数组如何初始化:
  • 在定义dp[i][j]含义的时候为什么要表示以下标i-1为结尾的字符串s,和以下标j-1为结尾的字符串t,相同子序列的长度为dp[i][j]
  • 因为这样的定义在dp二维矩阵中可以留出初始化的区间,如图:

image.png

  • 根据上图也可以看出,我们的遍历方向应该是从前到后,从上到小
  • 解题代码

  • 动态规划
  •     func isSubsequence(s string, t string) bool {
            dp := make([][]int,len(s)+1)
            for i:=0;i<len(dp);i++{
                dp[i] = make([]int,len(t)+1)
            }
            for i:=1;i<len(dp);i++{
                for j:=1;j<len(dp[i]);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[len(s)][len(t)]==len(s)
    
  • 指针
  •     func isSubsequence(s string, t string) bool {
            idx := 0
            i:=0
            j:=0
            for i<len(s) && j<len(t) {
                for j=idx;j<len(t);j++ {
                    if s[i] == t[j] {
                        i++
                        idx = j+1
                        break
                    }
                }
            }
        
            return i == len(s)
        }
    
  • 115不同的子序列

  • 代码随想录 (programmercarl.com)
  • 讲解观后感

  • 我们还是按照动态规划的固定解题顺序来解决。首先确定dp数组(dp table)以及下标的含义
    dp[i][j]:以i-1为结尾的s子序列中出现以j-1为结尾的t的个数为dp[i][j]。这样定义的原因是为了方便的初始化。
  • 在确定递推公式上本题较难理解。首先我们可以确定情况只有两种:
    • s[i - 1] 与 t[j - 1]相等
    • s[i - 1] 与 t[j - 1] 不相等
    • 我们先讨论相等的情况。相等时可以由两种状态推导而来。一种是用s[i - 1]来匹配,那么个数为dp[i - 1][j - 1]。代表着包含s[i-1]和t[j-1]这两个字符的相同子序列的数量。
      另一种是不用s[i - 1]来匹配,个数为dp[i - 1][j]。代表着在s[i-1]之前任意位置能够匹配t[j-1]的子序列的数量
    • 然后我们来看不相等的情况。不相等时,dp[i][j]只有一部分组成,不用s[i - 1]来匹配(就是模拟在s中删除这个元素),即:dp[i - 1][j]
  • dp数组如何初始化:从递推公式dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]; 和 dp[i][j] = dp[i - 1][j]; 中可以看出dp[i][j] 是从上方和左上方推导而来,,那么 dp[i][0] 和dp[0][j]是一定要初始化的。

image.png

  • dp[i][0] 表示:以i-1为结尾的s可以随便删除元素,出现空字符串的个数。那么dp[i][0]一定都是1。
  • dp[0][j]:空字符串s可以随便删除元素,出现以j-1为结尾的字符串t的个数。那么dp[0][j]一定都是0。
  • 注意dp[0][0]应该为1,因为空字符串s,可以删除0个元素,变成空字符串t。
  • 解题代码

  •     func numDistinct(s string, t string) int {
            dp:= make([][]int,len(s)+1)
            for i:=0;i<len(dp);i++{
                dp[i] = make([]int,len(t)+1)
            }
            // 初始化
            for i:=0;i<len(dp);i++{
                dp[i][0] = 1
            }
            // dp[0][j]0,默认值,因此不需要初始化
            for i:=1;i<len(dp);i++{
                for j:=1;j<len(dp[i]);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[len(s)][len(t)]
        }