力扣 3130 找出所有稳定的二进制数组 II

gomkiri 发布于 9 天前 14 次阅读


AI 摘要

如何用动态规划高效统计满足条件的二进制数组?本文详解力扣3130题的核心思路与代码实现,带你掌握处理连续子数组限制的巧妙方法。

题目:

给你 3 个正整数 zero ,one 和 limit 。

一个 二进制数组 arr 如果满足以下条件,那么我们称它是 稳定的 :

  • 0 在 arr 中出现次数 恰好 为 zero 。
  • 1 在 arr 中出现次数 恰好 为 one 。
  • arr 中每个长度超过 limit 的 子数组 都 同时 包含 0 和 1 。

请你返回 稳定 二进制数组的  数目。

由于答案可能很大,将它对 109 + 7 取余 后返回。

示例 1:

输入:zero = 1, one = 1, limit = 2

输出:2

解释:

两个稳定的二进制数组为 [1,0] 和 [0,1] ,两个数组都有一个 0 和一个 1 ,且没有子数组长度大于 2 。

示例 2:

输入:zero = 1, one = 2, limit = 1

输出:1

解释:

唯一稳定的二进制数组是 [1,0,1] 。

二进制数组 [1,1,0] 和 [0,1,1] 都有长度为 2 且元素全都相同的子数组,所以它们不稳定。

示例 3:

输入:zero = 3, one = 3, limit = 2

输出:14

解释:

所有稳定的二进制数组包括 [0,0,1,0,1,1] ,[0,0,1,1,0,1] ,[0,1,0,0,1,1] ,[0,1,0,1,0,1] ,[0,1,0,1,1,0] ,[0,1,1,0,0,1] ,[0,1,1,0,1,0] ,[1,0,0,1,0,1] ,[1,0,0,1,1,0] ,[1,0,1,0,0,1] ,[1,0,1,0,1,0] ,[1,0,1,1,0,0] ,[1,1,0,0,1,0] 和 [1,1,0,1,0,0] 。

思考

这个题实际上一上来就给了一个固定长度的数组,我们要做的是把确定数量的 0 和 1 放入数组中并其实符合条件。并且不难想到的是:
如果当前长度为 x ,可以组合的数量为 m,那么在加入一个 0 或 1,如果全部符合条件 , 可以组合的数量就是 2m,但是我们还有剔除所有不符合条件的情况,即有了连续 limit + 1 个 0 或 1。
既然是我们放入了第 x + 1 个数据后才导致的数据不符合条件,那么这一串连续的数据必然在数组的末尾,例如,如果我们最后放入一个 1 导致数据不合法了,那么目前的数组组成就是 :一串合法的数据 + 0 + limit+1个1 。所有我们要减去一串合法的数据 + 0 这一串数组可以组合的数量。

代码实现

class Solution {
    public int numberOfStableArrays(int zero, int one, int limit) {
        // 按照题目要求进行取模
        final int MOD = 1000000007;
        // 用 i 表示已经放入 0 的数量
        // 用 j 表示已经放入 1 的数量
        // 用 lastBit 表示当前数组的最后一位是 0 还是 1
        int[][][] dp = new int[zero + 1][one + 1][2];
        for(int i = 0 ; i <= zero; i ++){
            for(int j = 0;j <= one; j ++){
                for(int lastBit = 0 ;lastBit <= 1; lastBit ++){
                
                    // 初始化阶段
                    if(i == 0){
                        if(lastBit == 0 || j > limit){
                            // lastBit == 0 与 i == 0 两个条件互斥
                            // j > limit 且 i == 0 时这个数组一定是不合法的
                            dp[i][j][lastBit] = 0;
                        }else{
                            // 0 个 1 和 j 个 i,共组成了 1 个可能的情况(全是1)
                            dp[i][j][lastBit] = 1;
                        }
                    }else if(j == 0){ // j == 0 的初始化同理
                        if(lastBit == 1 || i > limit){
                            dp[i][j][lastBit] = 0;
                        }else {
                            dp[i][j][lastBit] = 1;
                        }
                        
                        
                    }else if(lastBit == 0){
                        // 先不考虑 数组不合法 的情况
                        dp[i][j][lastBit] =dp[i - 1][j][0] + dp[i - 1][j][1];
                        // 按照上面的分析进行扣减
                        if(i > limit){
                            dp[i][j][lastBit] -= dp[i - limit - 1][j][1];
                        }
                    }else {
                        dp[i][j][lastBit] = dp[i][j - 1][0] + dp[i][j - 1][1];
                        if(j > limit){
                            dp[i][j][lastBit] -=dp[i][j - limit - 1][0];
                        }
                    }
                    
                    
                    dp[i][j][lastBit] %= MOD;
                    if(dp[i][j][lastBit] < 0){
                        dp[i][j][lastBit] += MOD;
                    }
                }
            }
        }
        return (dp[zero][one][0] + dp[zero][one][1]) % MOD;
    }
}
Java