LeetCode-940. 不同的子序列 II【字符串,动态规划】
题目描述:LeetCode-940. 不同的子序列 II【字符串,动态规划】
给定一个字符串 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 仅由小写英文字母组成
解题思路一:细分问题,改为分别统计以 ‘a’,‘b’,⋯,‘z’ 结尾的不同子序列的个数,问题就迎刃而解了。每当遍历到下一个字母s[i]就更新以s[i]结尾的f[s[i]]的大小,其他不变。
例如:
aba
数组更新过程:
1 0
1 2
4 2
最后答案就是4+2=6
第一个数表示以a结尾的子串个数,第二个数表示以b结尾的子串个数…
遍历到第一个字符a更新第一个数为1
遍历到第二个字符b更新第二个数为1+0+1=2
遍历到第三个字符a更新第一个数为1+2+1=4
最终答案为f[n-1]的26个数字之和。
class Solution {
const int MOD = 1e9 + 7;
public:
int distinctSubseqII(string s) {
int n = s.length();
vector<array<int, 26>> f(n);
f[0][s[0] - 'a'] = 1;
for (int i = 1; i < n; ++i) {
f[i] = f[i - 1];
f[i][s[i] - 'a'] = accumulate(f[i - 1].begin(), f[i - 1].end(), 1L) % MOD;
}
return accumulate(f[n - 1].begin(), f[n - 1].end(), 0L) % MOD;
}
};
解题思路二:优化。只更新f[s[i]]和其他部分的值
以aba为例:
遍历到a时
others=0;
f[0]=1
total=1
遍历到b时
others=1;
f[1]=2;
total=3;
遍历到a时
others=2;
f[0]=4;
total=6;
最后return total
class Solution {
const int MOD = 1e9 + 7;
public:
int distinctSubseqII(string s) {
int total = 0, f[26]{};
for (char c : s) {
c -= 'a';
int others = total - f[c]; // total 中不含 f[c] 的部分(由于取模的原因,这里的减法可能会产生负数)
f[c] = 1 + total; // 更新 f[c]
total = ((f[c] + others) % MOD + MOD) % MOD; // 更新 total,并保证 total 非负
}
return total;
}
};
解题思路三:只需要更新 f[s[i]] 的值
class Solution {
const int MOD = 1e9 + 7;
public:
int distinctSubseqII(string s) {
int f[26]{};
for (char c : s)
f[c - 'a'] = accumulate(f, f + 26, 1L) % MOD;
return accumulate(f, f + 26, 0L) % MOD;
}
};
时间复杂度:O(n+∣Σ∣),其中 n 为 s 的长度。
空间复杂度:O(∣Σ∣)其中∣Σ∣ 为字符集合的大小,本题中字符均为小写字母,所以 ∣Σ∣=26|
解题思路三:最终的最简代码
class Solution {
const int MOD = 1e9 + 7;
public:
int distinctSubseqII(string s) {
int total=0,f[26]{};
for(char c:s){
c-='a';
int others=total-f[c];
f[c]=total+1;
total=((others+f[c])%MOD+MOD)%MOD;
}
return total;
}
};
参考链接:参考链接
相关文章
- Java实现 LeetCode 808 分汤 (暴力模拟)
- Java实现 LeetCode 887 鸡蛋掉落(动态规划,谷歌面试题,蓝桥杯真题)
- Java实现 LeetCode 714 买卖股票的最佳时机含手续费(动态规划 || 迭代法)
- Java实现 LeetCode 718 最长重复子数组(动态规划)
- Java实现 LeetCode 629 K个逆序对数组(动态规划+数学)
- Java实现 LeetCode 629 K个逆序对数组(动态规划+数学)
- Java实现 LeetCode 609 在系统中查找重复文件(阅读理解+暴力大法)
- Java实现 LeetCode 1162 地图分析(可以暴力或者动态规划的BFS)
- Java实现 LeetCode 556 下一个更大元素 III(数组的翻转)
- Java实现 LeetCode 518 零钱兑换 II
- Java实现 LeetCode 397 整数替换
- 每日一道 LeetCode (51):盛最多水的容器
- [LeetCode] Single Number III
- [LeetCode] Binary Tree Postorder Traversal
- 【数组&双指针】LeetCode 76. 最小覆盖子串【困难】
- LeetCode-357. 统计各位数字都不同的数字个数【排列组合,递归,动态规划】
- Leetcode学习计划之动态规划入门day9(139,42)
- Leetcode学习计划之动态规划入门day4(55,45)
- Leetcode 2270. 分割数组的方案数(可以,已解决)
- [LeetCode] 516. 最长回文子序列 ☆☆☆(动态规划)
- [LeetCode] 42. 接雨水 ☆☆☆☆☆(按列、动态规划、双指针)
- [LeetCode] 303. 区域和检索 - 数组不可变 ☆(动态规划)
- [LeetCode] 120. 三角形最小路径和 ☆☆☆(动态规划 范例)
- [LeetCode] 63. 不同路径 II ☆☆☆(动态规划)
- leetcode dfs Flatten Binary Tree to Linked List
- 【Leetcode刷题Python】94. 二叉树的中序遍历
- 【Leetcode刷题Python】53. 最大子数组和
- 【Leetcode刷题Python】高效求递归中函数的调用次数(动态规划方法,顺丰笔试题)
- 【LeetCode】剑指 Offer 51. 数组中的逆序对