1955. 统计特殊子序列的数目
特殊序列 是由 正整数 个 0 ,紧接着 正整数 个 1 ,最后 正整数 个 2 组成的序列。
比方说,[0,1,2] 和 [0,0,1,1,1,2] 是特殊序列。
相反,[2,1,0] ,[1] 和 [0,1,2,0] 就不是特殊序列。
给你一个数组 nums (仅 包含整数 0,1 和 2),请你返回 不同特殊子序列的数目 。由于答案可能很大,请你将它对 109 + 7 取余 后返回。
一个数组的 子序列 是从原数组中删除零个或者若干个元素后,剩下元素不改变顺序得到的序列。如果两个子序列的 下标集合 不同,那么这两个子序列是 不同的 。
示例 1:
输入:nums = [0,1,2,2]
输出:3
解释:特殊子序列为 [0,1,2,2],[0,1,2,2] 和 [0,1,2,2] 。
f[i][0] 表示前 i 项得到的全 0 子序列个数
f[i][1] 表示前 i 项得到的先 0 后 1 的子序列个数
f[i][2] 表示前 i 项得到的特殊子序列个数
若 j =nums[i] 则需要分类计算:
当遇到 0 时,有选或不选两种方案,不选 0 时有 f[i][0] = f[i-1][0],选 0 时,可以单独组成一个子序列,也可以与前面的 0 组合,因此有 f[i][0] = f[i-1][0] + 1,两者相加得 f[i][0] = 2⋅f[i−1][0]+1。
当遇到 1 时,有选或不选两种方案,不选 1 时有 f[i][1] = f[i−1][1],选 1 时,可以单独与前面的 0 组成一个子序列,也可以与前面的 1 组合,因此有 f[i][1] = f[i-1][1] + f[i-1][0],两者相加得 f[i][1]=2⋅f[i−1][1]+f[i−1][0]。
当遇到 2 时,有选或不选两种方案,f[i][2] 和 f[i][1] 类似,有 f[i][2]=2⋅f[i−1][2]+f[i−1][1]。
最后答案为 f[n-1][2]f[n−1][2]。
代码: (可以再压缩一下,只和上一个状态相关,用三个变量即可)
class Solution { public: int countSpecialSubsequences(vector<int>& nums) { long long dp[100005][3]; long long mod=1e9+7; dp[0][0]=0; dp[0][1]=0; dp[0][2]=0; if (nums[0]==0) dp[0][0]=1; for(int i=1;i<nums.size();i++) { if(nums[i]==0) { dp[i][0]=(dp[i-1][0]*2+1)%mod; dp[i][1]=dp[i-1][1]; dp[i][2]=dp[i-1][2]; } if(nums[i]==1) { dp[i][0]=dp[i-1][0]; dp[i][1]=(dp[i-1][0]+dp[i-1][1]*2)%mod; dp[i][2]=dp[i-1][2]; } if(nums[i]==2) { dp[i][0]=dp[i-1][0]; dp[i][1]=dp[i-1][1]; dp[i][2]=(dp[i-1][1]+dp[i-1][2]*2)%mod; } } return dp[nums.size()-1][2]%mod; } };
class Solution { public: int countSpecialSubsequences(vector<int>& nums) { long long mod=1e9+7; long long f[3]; f[0]=0;f[1]=0;f[2]=0; for (auto v:nums) { if (v == 0) f[0] = (f[0]*2 + 1) % mod; else if (v == 1) f[1] = (f[1]*2 + f[0]) % mod; else f[2] = (f[2]*2 + f[1]) % mod; } return f[2]; } };