leetcode 940. 不同的子序列 II

67 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第12天,点击查看活动详情

1. 题目与解析

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

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

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

输入: s = "abc"

输出: 7

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

输入: s = "aba"

输出: 6

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

输入: s = "aaa"

输出: 3

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

考虑使用动态规划的方式来解题。

对于第i位,我们需要考虑如何通过前i-1位转移过来:

  • 首先,如果只考虑第i-1位的话,直接拼接上第i位,会存在重复的问题,因此需要考虑负责状态转移的方式。
  • 这里我们考虑一种特殊情况,比如说第i位以及第i+n位都是以a结尾,那么第i+n位的所有子序列是可以完全覆盖第i位的,因此我们考虑用一个列表dp来维护以每一个字母结尾的自序列的数量。
  • 在状态转移时,对于第i位,我们只需要考虑在这之前的dp列表所存在的所有自序列拼接上第i位的字母以及其本身即可。
  • 最后需要返回答案时,我们只需要将整个dp列表的结果相加返回就可以了。
  • 另外需要注意dp列表的初始化即可。

2. 题解

class Solution {
    public int distinctSubseqII(String s) {
        int mod = 1000000007;
        int[] dp = new int[26];
        Arrays.fill(dp, -1);

        for (int i = 0; i < s.length(); i++) {
            int num = s.charAt(i) - 'a', tmp = 1;
            for (int j = 0; j < 26; j++) {
                if (dp[j] != -1) {
                    tmp = (tmp + dp[j]) % mod;
                }
            }
            dp[num] = tmp;
        }

        int ans = 0;
        for (int j = 0; j < 26; j++) {
            if (dp[j] != -1) {
                ans = (ans + dp[j]) % mod;
            }
        }
        return ans;
    }
}