Leetcode题解 940.不同的子序列II 动态规划

题目描述:

给定一个字符串 s,计算 s 的 不同非空子序列 的个数。因为结果可能很大,所以返回答案需要对 10^9 + 7 取余 。

字符串的 子序列 是经由原字符串删除一些(也可能不删除)字符但不改变剩余字符相对位置的一个新字符串。

  • 例如,"ace" 是 "abcde" 的一个子序列,但 "aec" 不是。

示例 1:

输入:s = "abc"
输出:7
解释:7 个不同的子序列分别是 "a", "b", "c", "ab", "ac", "bc", 以及 "abc"。

示例 2:

输入:s = "aba"
输出:6
解释:6 个不同的子序列分别是 "a", "b", "ab", "ba", "aa" 以及 "aba"。

示例 3:

输入:s = "aaa"
输出:3
解释:3 个不同的子序列分别是 "a", "aa" 以及 "aaa"。

提示:

  • 1 <= s.length <= 2000
  • s 仅由小写英文字母组成

思路:

要计算字符串 s 的不同非空子序列的个数,我们可以采用动态规划的思路。基本思想是:

  1. 使用一个数组 dp,其中 dp[i] 表示前 i 个字符的不同非空子序列的个数。
  2. 对于每一个字符 s[i],我们可以基于之前的所有子序列构造出新的子序列。因此,dp[i] 等于 dp[i-1] 的两倍减去因重复导致的重复子序列数。
  3. 为了处理重复子序列,我们需要跟踪每个字符上次出现的位置。我们可以使用一个字典 last_occurrence 来记录字符最后出现的位置。

下面是具体的 Python 实现代码:

class Solution:
    def distinctSubseqII(self, s: str) -> int:
        
        MOD = 10**9 + 7
        n = len(s)
        
        # dp[i] 表示前 i 个字符的不同非空子序列的个数
        dp = [0] * (n + 1)
        
        # 用于记录每个字符最后一次出现的位置
        last_occurrence = {}
        
        # 初始状态
        dp[0] = 1  # 空序列
        
        for i in range(1, n + 1):
            dp[i] = (2 * dp[i - 1]) % MOD  # 当前字符s[i-1]可以在之前的所有子序列基础上构造新的子序列
            
            char = s[i - 1]
            if char in last_occurrence:
                # 减去由于重复出现而造成的重复子序列
                dp[i] = (dp[i] - dp[last_occurrence[char] - 1]) % MOD
            
            # 更新字符的最后出现位置
            last_occurrence[char] = i
        
        # 减去空序列
        return (dp[n] - 1) % MOD

'''
#示例
s = "abc"
print(distinct_subsequences(s))
'''

 代码解析:

  • dp[i] 表示前 i 个字符的不同非空子序列的个数。
  • last_occurrence 记录每个字符上次出现的位置。
  • 对于每个字符,我们将 dp[i] 更新为前一状态的两倍,表示在现有子序列的基础上增加新子序列。
  • 如果该字符在之前出现过,减去因为重复出现而多计算的子序列数。
  • 最后返回 dp[n] - 1,减去空序列,并对结果取模。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值