leetcode 2026-03-09 每日一练 - 找出所有稳定的二进制数组 I

leetcode 2026-03-09 每日一练 - 找出所有稳定的二进制数组 I
Austoin题目描述
LeetCode 3129. 找出所有稳定的二进制数组 I
给你 3 个正整数 zero、one 和 limit。
如果一个二进制数组满足:
- 0 的个数恰好是
zero - 1 的个数恰好是
one - 任意长度大于
limit的子数组都同时包含 0 和 1
则称该数组是稳定的。
请返回稳定二进制数组的总数,答案对 1e9+7 取模。
示例 1:
- 输入:
zero = 1, one = 1, limit = 2 - 输出:
2
示例 2:
- 输入:
zero = 1, one = 2, limit = 1 - 输出:
1
示例 3:
- 输入:
zero = 3, one = 3, limit = 2 - 输出:
14
解题思路
这题本质是一个“带连续段限制”的计数 DP。
方法一:记忆化搜索
定义 dfs(i, j, k):
- 还剩
i个 0、j个 1 可用 - 当前这个位置要填的数字是
k(k=0或k=1) - 返回满足条件的方案数
最终答案:
dfs(zero, one, 0) + dfs(zero, one, 1)。
关键转移(以 k=0 为例):
- 正常拼接来源:
dfs(i-1, j, 0)和dfs(i-1, j, 1) - 但其中包含了“连续 0 超过 limit”的非法情况
- 非法部分正好是:倒数
limit+1个都是 0,即要减去dfs(i-limit-1, j, 1)
k=1 对称处理即可。
代码实现(方法一:记忆化搜索)
1 | func numberOfStableArrays(zero int, one int, limit int) int { |
方法二:动态规划
把记忆化搜索改写成迭代 DP。
定义 f[i][j][k]:
- 使用
i个 0、j个 1 - 且最后一个数字是
k - 的稳定数组数量
答案是 f[zero][one][0] + f[zero][one][1]。
初始化:
f[i][0][0] = 1,1 <= i <= min(limit, zero)f[0][j][1] = 1,1 <= j <= min(limit, one)
转移方程:
f[i][j][0] = f[i-1][j][0] + f[i-1][j][1] - f[i-limit-1][j][1]f[i][j][1] = f[i][j-1][0] + f[i][j-1][1] - f[i][j-limit-1][0]
同样注意取模与负数修正。
核心细节
- 状态定义里保留最后一位/当前位信息:否则没法判断是否连续超过
limit。 - “减去非法项”是关键:这题最容易漏掉这一步,导致把超长连续段也计入答案。
- 取模防负数:
(a - b + mod) % mod不能省。 - 边界
i<0或j<0返回 0:避免越界状态污染结果。
复杂度分析
设 Z = zero,O = one。
- 时间复杂度:
O(Z * O) - 空间复杂度:
O(Z * O)
记忆化和 DP 本质状态数一致,复杂度同阶。
边界情况与自测用例
建议至少覆盖这些测试:
zero = 1, one = 1, limit = 1,答案应为2(01、10)。zero = 3, one = 0, limit = 2,答案应为0(连续 0 超限)。zero = 2, one = 0, limit = 2,答案应为1(00)。zero = 0, one = 3, limit = 2,答案应为0(连续 1 超限)。zero = 0, one = 2, limit = 2,答案应为1(11)。
这些用例可以快速验证:
- 边界是否正确
- 减法去重是否正确
- 模运算是否安全
总结
这题是“计数 + 连续段限制”的经典模型。
记忆化搜索更直观,动态规划更适合模板化实现。两者核心都在于:
- 按最后一位拆状态
- 用“总来源 - 非法来源”控制连续段长度
参考资料
- 作者:ylb
- 链接:
https://leetcode.cn/problems/find-all-possible-stable-binary-arrays-i/solutions/2870488/python3javacgotypescript-yi-ti-shuang-ji-vhlz/ - 来源:力扣(LeetCode)













