“我觉得坦途在前,人又何必因为一点小障碍而不走路呢?”——鲁迅
题目
给你 3 个正整数 zero
,one
和 limit
。
一个 二进制数组
arr
如果满足以下条件,那么我们称它是 稳定的 :
- 0 在
arr
中出现次数 恰好 为zero
。 - 1 在
arr
中出现次数 恰好 为one
。 arr
中每个长度超过limit
的子数组 都 同时 包含 0 和 1 。
请你返回 稳定 二进制数组的 总 数目。
由于答案可能很大,将它对取余 后返回。
难度:困难
分析
笔者本来在做找出所有稳定的二进制数组 Ⅰ(中等题)却觉得异常困难😓,发现这两道题的唯一区别就是数据量,那么就记录这道题吧。不过,笔者的笨方法可以通过Ⅰ,Ⅱ中内存超出限制,所以还是借鉴了官方的题解。
我们使用dp[zero][one][0]表示含有zero个0,one个1且末尾为0的数组个数,dp[zero][one][1]表示含有zero个0,one个1且末尾为1的数组个数。对于dp[zero][one][0],如果不考虑limit的限制,则当前是0,前面是0或1都可,易知dp[zero][one][0]=dp[zero-1][one][0]+dp[zero-1][one][1],考虑limit,则需要减去添加末尾0后导致数组不稳定的情况,数组从稳定变为不稳定临界情况为之前恰好有limit个0,我们把这些0固定,如果前面还有数组,它的数量为dp[zero-limit-1][one][1](注意末尾是1)。dp[zero][one][1]亦然,不再赘述。
综上所述可以得到状态转移方程:
最后注意边界情况,个数不超过limit为1,否则为0。
解答
class Solution {
public:
int numberOfStableArrays(int zero, int one, int limit) {
int MOD=1e9+7;
vector<vector<vector<int>>> dp(zero+1,vector<vector<int>>(one+1,vector<int>(2)));
//one为0的边界情况
for (int i=0;i<=min(zero,limit);i++){
dp[i][0][0]=1;
}
//zero为0的边界情况
for (int j=0;j<=min(one,limit);j++){
dp[0][j][1]=1;
}
for (int i=1;i<=zero;i++){
for (int j=1;j<=one;j++){
//处理末尾为0的情况
dp[i][j][0]=dp[i-1][j][0]+dp[i-1][j][1];
if (i>limit){
dp[i][j][0]-=dp[i-limit-1][j][1];
}
//处理末尾为1的情况
dp[i][j][1]=dp[i][j-1][0]+dp[i][j-1][1];
if (j>limit){
dp[i][j][1]-=dp[i][j-limit-1][0];
}
//取余
dp[i][j][0]%=MOD;
if (dp[i][j][0]<0){
dp[i][j][0]+=MOD;
}
dp[i][j][1]%=MOD;
if (dp[i][j][1]<0){
dp[i][j][1]+=MOD;
}
}
}
return (dp[zero][one][0]+dp[zero][one][1])%MOD;
}
};