不同的子序列 II
难度:
标签:
题目描述
代码结果
运行时间: 36 ms, 内存: 16.0 MB
/*
* 思路:
* 1. 使用动态规划结合Java Stream API。
* 2. 用一个数组dp记录前i个字符的不同非空子序列个数。
* 3. 使用一个数组last来记录每个字符上一次出现的位置。
* 4. 最终答案需要对10^9 + 7取余。
*/
import java.util.stream.IntStream;
public class Solution {
public int distinctSubseqII(String s) {
int MOD = 1000000007;
int n = s.length();
int[] dp = new int[n + 1];
int[] last = new int[26];
dp[0] = 1;
IntStream.range(1, n + 1).forEach(i -> {
char c = s.charAt(i - 1);
dp[i] = (dp[i - 1] * 2 % MOD - (last[c - 'a'] > 0 ? dp[last[c - 'a'] - 1] : 0) + MOD) % MOD;
last[c - 'a'] = i;
});
return dp[n] - 1;
}
}
解释
方法:
这个题解使用了动态规划的思想来计算所有的不同子序列。具体方法是用一个数组endCnt来记录以每个字符结尾的子序列的数量。对于字符串s中的每个字符x,当前所有的子序列可以与x组合形成新的子序列,而新形成的以x结尾的子序列的数量就是在x加入之前的所有子序列数量减去上一次以x结尾的子序列数量。这样可以保证计数的过程中不会重复计算相同的子序列。最终结果需要减去初始化时候计入的空子序列。
时间复杂度:
O(n)
空间复杂度:
O(1)
代码细节讲解
🦆
为什么初始化ans为1而不是0?这是否代表已经计入了空子序列?
▷🦆
在更新endCnt数组时,为何需要从ans减去上一次以该字符结尾的子序列数量?这样的操作有什么特别的意义或目的?
▷🦆
题解中提到,为了避免重复计算相同的子序列,需要进行某些减法操作。这个逻辑是如何确保算法的正确性的?
▷🦆
题解最后返回的结果为何要进行(ans - 1 + mod) % mod这样的操作?直接返回ans - 1不行吗?
▷