【力扣roadmap】1955. 统计特殊子序列的数目

11 阅读2分钟

题目描述

image.png

思路

我们列出如下三个定义

dp[i][0]表示到下标i为止,全0序列的数目dp[i][1]表示到下标i为止,先01的序列的数目dp[i][2]表示到下标i为止,先012的序列的数目dp[i][0]表示到下标i为止,全0序列的数目 \\ dp[i][1]表示到下标i为止,先0后1的序列的数目 \\ dp[i][2]表示到下标i为止,先0中1后2的序列的数目

显然最终答案应该是dp[n-1][2]。all right,怎么进行状态转移呢? 你发现,只有遇到了0才能更新dp[i][0];只有遇到1才能更新dp[i][1];只有遇到2才能更新dp[i][2]

  • 如果nums[i]==0 , 意味着前面长度为i-1的序列后面添了一个0,这不会影响到“先0后1”的序列数量,也不会影响“先0中1后2”的序列数量。那么dp[i][0]怎么更新呢?dp[i][0]可以被看成三部分的组合
    • 假装没有新来的0,那么”全0“的序列数量为dp[i-1][0]
    • 旧”全0序列“中每个方案都搭配上这个新来的0,序列数量也是dp[i-1][0]
    • 旧”全0序列“方案全不要,只留这个新来的0,方案数为1
    • 综上,dp[i][0] = dp[i-1][0] * 2 + 1
  • 如果nums[i]==1 , 意味着前面长度为i-1的序列后面添了一个1,这不会影响到”全0序列”数量,也不会影响”先0中1后2“的序列数量。那么dp[i][1]怎么更新呢?dp[i][1]可以被看成三部分的组合
    • 假装没有新来的1,那么“先0后1”的序列数量为dp[i-1][1]
    • 旧”先0后1序列“中每个方案都搭配上这个新来的1,序列数量也是dp[i-1][1]
    • 旧的1全不要,只留这个新来的1,那么“先0后1”的方案数为dp[i-1][0]
    • 综上,dp[i][1] = dp[i-1][1] * 2 + dp[i-1][0]
  • 如果nums[i]==2 , 意味着前面长度为i-1的序列后面添了一个2,这不会影响到”全0序列”数量,也不会影响“先0后1”的序列数量。那么dp[i][2]怎么更新呢?dp[i][2]可以被看成三部分的组合
    • 假装没有新来的2,那么”先0中1后2“的序列数量为dp[i-1][2]
    • 旧”先0中1后2序列“中每个方案都搭配上这个新来的2,序列数量也是dp[i-1][2]
    • 旧的2全不要,只留这个新来的2,那么"先0中1后2"的方案数为dp[i-1][1]
    • 综上,dp[i][2] = dp[i-1][2] * 2 + dp[i-1][1]

代码

class Solution:
    def countSpecialSubsequences(self, nums: List[int]) -> int:
        
        MOD = int(1e9+7) 
        dp = [0] * 3 
        for x in nums :
            if x == 0 :
                dp[0] = (dp[0] * 2 + 1) % MOD 
            elif x == 1 :
                dp[1] = (dp[1] * 2 + dp[0]) % MOD 
            else :
                dp[2] = (dp[2] * 2 + dp[1]) % MOD 
        return dp[2]