Find All Possible Stable Binary Arrays II
Find All Possible Stable Binary Arrays II
You are given 3 positive integers zero
, one
, and limit
.
A binary array arr
is called stable if:
- The number of occurrences of 0 in
arr
is exactlyzero
. - The number of occurrences of 1 in
arr
is exactlyone
. - Each subarray of
arr
with a size greater thanlimit
must contain both 0 and 1.
Return the total number of stable binary arrays.
Since the answer may be very large, return it modulo 109 + 7
.
Example 1:
Input: zero = 1, one = 1, limit = 2
Output: 2
Explanation:
The two possible stable binary arrays are [1,0]
and [0,1]
.
Example 2:
Input: zero = 1, one = 2, limit = 1
Output: 1
Explanation:
The only possible stable binary array is [1,0,1]
.
Example 3:
Input: zero = 3, one = 3, limit = 2
Output: 14
Explanation:
All the possible stable binary arrays are [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]
, and [1,1,0,1,0,0]
.
Constraints:
1 <= zero, one, limit <= 1000
解题思路
一开始状态定义的不好,导致优化起来巨复杂。为了方便这里重新定义符号 ,,,。
容易知道当 的位置确定后, 的位置也就确定了,所以我们可以只关注 的位置。定义 表示在前 个位置中放入了 个 且第 个位置是 的方案数。根据最后一段的 的个数及其前一段 的个数进行状态划分。
具体来说如果最后一段有 个 ,显然 ,其前一段 的个数为 ,同样满足 ,则有 。所以状态转移方程就是:
显然直接暴力 dp 的话时间复杂度为 。我们把上面的式子展开:
可以发现有点类似于平行四边形。再考虑 :
与 相比 只多出了红色的部分,可以看作是以 为端点的对角线上 个 的和,即 。另外减少的部分是 。
为此我们可以维护一个 ,转移方程为 。
这样当我们确定 再枚举 时,可以维护一个大小为 的滑动窗口,并用 来维护窗口内的 的和。当枚举到 和 ,有 ,然后有 。
另外边界情况,即枚举到 时前 个位置均为 ,此时有 ,当然只有 时才成立。剩下的细节见代码。
最后根据最后一段 的不同个数来统计答案,即 。
AC 代码如下,时间复杂度为 :
class Solution {
public:
int mod = 1e9 + 7;
int numberOfStableArrays(int a, int b, int k) {
int n = a + b;
vector<vector<int>> f(b + 1, vector<int>(n + 1)), g(b + 1, vector<int>(n + 1));
f[0][0] = g[0][0] = 1;
for (int i = 1; i <= b; i++) {
if (i <= k) f[i][i] = 1;
for (int j = i + 1, s = 0; j <= n; j++) {
s = (s + g[i - 1][j - 2]) % mod;
if (j - k - 2 >= 0) s = (s - g[i - 1][j - k - 2]) % mod;
f[i][j] = s;
}
// g[i][0] = f[i][0];
for (int j = 1; j <= n; j++) {
g[i][j] = (g[i - 1][j - 1] + f[i][j]) % mod;
if (i - k >= 0 && j - k >= 0) g[i][j] = (g[i][j] - f[i - k][j - k]) % mod;
}
}
int ret = 0;
for (int i = 0; i <= k && i <= n; i++) {
ret = (ret + f[b][n - i]) % mod;
}
ret = (ret + mod) % mod;
return ret;
}
};
事实上如果把状态定义成 表示放置了 个 , 个 且最后一段是 的方案数,那么就会变得十分简单了。反正我是想不到的,关键点应该是要发现对于任意一个合法方案,每一段的 和 是交替出现的。
状态转移方程就很简单了。
优化的地方就很容易看到了,也是用一个大小为 的滑动窗口来维护 。其中对于 ,应该在每一列开一个单调队列。而对于 ,则应该在每一行开一个单调队列。
最后答案就是 。
AC 代码如下,时间复杂度为 :
class Solution {
public:
int mod = 1e9 + 7;
int numberOfStableArrays(int a, int b, int k) {
vector<vector<vector<int>>> f(a + 1, vector<vector<int>>(b + 1, vector<int>(2)));
vector<int> c(b + 1), r(a + 1);
for (int i = 1; i <= k && i <= a; i++) {
f[i][0][0] = 1;
}
for (int i = 1; i <= k && i <= b; i++) {
f[0][i][1] = 1;
}
for (int i = 1; i <= a; i++) {
for (int j = 1; j <= b; j++) {
if (i - 1 >= 0) c[j] = (c[j] + f[i - 1][j][1]) % mod;
if (i - k - 1 >= 0) c[j] = (c[j] - f[i - k - 1][j][1]) % mod;
f[i][j][0] = c[j];
if (j - 1 >= 0) r[i] = (r[i] + f[i][j - 1][0]) % mod;
if (j - k - 1 >= 0) r[i] = (r[i] - f[i][j - k - 1][0]) % mod;
f[i][j][1] = r[i];
}
}
return ((f[a][b][0] + f[a][b][1]) % mod + mod) % mod;
}
};
参考资料
第 129 场力扣夜喵双周赛 - 力扣(LeetCode):https://leetcode.cn/circle/discuss/SZyTo4/view/mv9LwV/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18164789
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2023-04-28 D. Unique Palindromes