持续创作,加速成长!这是我参与「掘金日新计划 · 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;
}
}