题目描述:
给定一个字符串
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
的不同非空子序列的个数,我们可以采用动态规划的思路。基本思想是:
- 使用一个数组
dp
,其中dp[i]
表示前i
个字符的不同非空子序列的个数。- 对于每一个字符
s[i]
,我们可以基于之前的所有子序列构造出新的子序列。因此,dp[i]
等于dp[i-1]
的两倍减去因重复导致的重复子序列数。- 为了处理重复子序列,我们需要跟踪每个字符上次出现的位置。我们可以使用一个字典
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
,减去空序列,并对结果取模。