本文涉及知识点
C++动态规划
LeetCode1955. 统计特殊子序列的数目
特殊序列 是由 正整数 个 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] 。
示例 2:
输入:nums = [2,2,0,0]
输出:0
解释:数组 [2,2,0,0] 中没有特殊子序列。
示例 3:
输入:nums = [0,1,2,0,1,2]
输出:7
解释:特殊子序列包括:
- [0,1,2,0,1,2]
- [0,1,2,0,1,2]
- [0,1,2,0,1,2]
- [0,1,2,0,1,2]
- [0,1,2,0,1,2]
- [0,1,2,0,1,2]
- [0,1,2,0,1,2]
提示:
1 <= nums.length <= 105
0 <= nums[i] <= 2
动态规划
n = nums.length
动态规划的状态表示
dp[i][j] 表示nums[0…i]中以j结尾的特殊子序列的数目。
动态规划的填表顺序
枚举后续状态,i = 1 to n-1
动态规划的转移方程
如果不选择nums[i] ,dp[i] = dp[i-1]
选择:
j = nums
dp[i][j] += dp[i-1][max(0,j-1)…j]之和。
如果j是0 dp[i][j]+1
动态规划的初始化
dp[0][nums[0]]=1,其它全部为0。
动态规划的返回值
dp.back().back()
代码
核心代码
template<int MOD = 1000000007>
class C1097Int
{
public:
C1097Int(long long llData = 0) :m_iData(llData% MOD)
{
}
C1097Int operator+(const C1097Int& o)const
{
return C1097Int(((long long)m_iData + o.m_iData) % MOD);
}
C1097Int& operator+=(const C1097Int& o)
{
m_iData = ((long long)m_iData + o.m_iData) % MOD;
return *this;
}
C1097Int& operator-=(const C1097Int& o)
{
m_iData = (m_iData + MOD - o.m_iData) % MOD;
return *this;
}
C1097Int operator-(const C1097Int& o)
{
return C1097Int((m_iData + MOD - o.m_iData) % MOD);
}
C1097Int operator*(const C1097Int& o)const
{
return((long long)m_iData * o.m_iData) % MOD;
}
C1097Int& operator*=(const C1097Int& o)
{
m_iData = ((long long)m_iData * o.m_iData) % MOD;
return *this;
}
C1097Int operator/(const C1097Int& o)const
{
return *this * o.PowNegative1();
}
C1097Int& operator/=(const C1097Int& o)
{
*this /= o.PowNegative1();
return *this;
}
bool operator==(const C1097Int& o)const
{
return m_iData == o.m_iData;
}
bool operator<(const C1097Int& o)const
{
return m_iData < o.m_iData;
}
C1097Int pow(long long n)const
{
C1097Int iRet = 1, iCur = *this;
while (n)
{
if (n & 1)
{
iRet *= iCur;
}
iCur *= iCur;
n >>= 1;
}
return iRet;
}
C1097Int PowNegative1()const
{
return pow(MOD - 2);
}
int ToInt()const
{
return m_iData;
}
private:
int m_iData = 0;;
};
class Solution {
public:
int countSpecialSubsequences(vector<int>& nums) {
const int N = nums.size();
vector<vector<C1097Int<>>> dp(N, vector<C1097Int<>>(3));
auto Add = [&](int i) {
const int j = nums[i];
if (0 == j) { dp[i][j] += 1; };
};
Add(0);
vector<vector<C1097Int<>>> dp2(N, vector<C1097Int<>>(3));
for (int i = 1; i < N; i++) {
dp[i] = dp[i - 1];
const int j = nums[i];
for (int k = max(0,j-1); k <= j; k++) {
dp[i][j] += dp[i - 1][k];
}
Add(i);
}
return dp.back().back().ToInt();
}
};
核心代码
vector<int> nums;
TEST_METHOD(TestMethod11)
{
nums = { 0, 1, 2, 2 };
auto res = Solution().countSpecialSubsequences(nums);
AssertEx(3, res);
}
TEST_METHOD(TestMethod12)
{
nums = { 2,2,0,0 };
auto res = Solution().countSpecialSubsequences(nums);
AssertEx(0, res);
}
TEST_METHOD(TestMethod13)
{
nums = { 0,1,2,0,1,2 };
auto res = Solution().countSpecialSubsequences(nums);
AssertEx(7, res);
}