持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第15天,点击查看活动详情
前言
今天的题目为困难,题目有点难度,参考了题解中的动态规划的解法,通过统计每个字符结尾的出现次数这种方式来完成了这道题
每日一题
今天的题目是 940. 不同的子序列 II,难度为困难
-
给定一个长度为 n 的整数数组 arr ,它表示在 [0, n - 1] 范围内的整数的排列。
-
给定一个字符串 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 仅由小写英文字母组成
题解
动态规划
通过动态规划来解题,首先我们定义数组dp,dp[i]为以不同字母结尾的子序列
那一个字符串 asd 来进行演示,每一个单独的字符可以作为一个子序列,子序列添加一个字符可以成为一个新的子序列,一直到子序列的长度和字符串相同。
于是,我们可以从第一个字符开始,去统计每一个字符结尾的子序列拥有多少个,
那么,对于 asdff, 逐个遍历字符,考虑将当前字符作为结尾,然后在前面添加字符:
a: a s: as, s d: ad, asd, sd, d f: af, asf, sf, adf, asdf, sdf, df, f f: af, asf, sf, adf, asdf, sdf, df, f, aff, asf, sff, adff, asdff, sdff, dff, ff, f
对于以字符 'f' 结尾的子序列,可以由以下几个部分组成:
-
自身作为一个字符
-
所有前面字符结尾的子序列 + 当前字符
但是又发现,当字符重复时,以 f 结尾的序列包含了前一个,所以我们只需要统计最后一个为 f 的子序列数量。
所以每个结尾的个数为 当前遍历字符作为一个子串 + 前面最后一次出现的字符的数量之和
1 + sum(前面的子序列)
const MOD=1000000007
function distinctSubseqII(s: string): number {
let dp=new Array(26).fill(0)
let sum=0
for(let i=0;i<s.length;i++){
let temp= dp[s[i].charCodeAt(0)-'a'.charCodeAt(0)]
dp[s[i].charCodeAt(0)-'a'.charCodeAt(0)]=sum+1
sum+=(sum+1-temp)%MOD
}
return sum % MOD
};