leetcode刷题记录-940. 不同的子序列 II

112 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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' 结尾的子序列,可以由以下几个部分组成:

  1. 自身作为一个字符

  2. 所有前面字符结尾的子序列 + 当前字符

但是又发现,当字符重复时,以 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
};

image.png