474.一和零

给你一个二进制字符串数组 strs 和两个整数 mn

请你找出并返回 strs 的最大子集的长度,该子集中 最多m0n1

如果 x 的所有元素也是 y 的元素,集合 x 是集合 y子集

示例 1:

1
2
3
4
输入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
输出:4
解释:最多有 5031 的最大子集是 {"10","0001","1","0"} ,因此答案是 4
其他满足题意但较小的子集包括 {"0001","1"} 和 {"10","1","0"} 。{"111001"} 不满足题意,因为它含 41 ,大于 n 的值 3

示例 2:

1
2
3
输入:strs = ["10", "0", "1"], m = 1, n = 1
输出:2
解释:最大的子集是 {"0", "1"} ,所以答案是 2

Solution

显然,这是一个二维的01背包问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Solution {

// 主方法,找到最多由 m 个 0 和 n 个 1 组成的字符串数量
public int findMaxForm(String[] strs, int m, int n) {
// 初始化动态规划数组 dp,大小为 (m+1) x (n+1)
int[][] dp = new int[m + 1][n + 1];
dp[0][0] = 0; // 初始状态:0 个 0 和 0 个 1 时,可以组成的字符串数量为 0

// 遍历每个字符串
for (String s : strs) {
// 计算当前字符串中的 0 和 1 的数量
int[] zeroAndOne = calcZeroAndOne(s);
int zeros = zeroAndOne[0];
int ones = zeroAndOne[1];

// 倒序遍历 dp 数组,防止重复使用同一个字符串
for (int i = m; i >= zeros; i--) {
for (int j = n; j >= ones; j--) {
// 更新 dp 数组,选择使用当前字符串和不使用当前字符串中的最大值
dp[i][j] = Math.max(dp[i][j], dp[i - zeros][j - ones] + 1);
}
}
}

// 返回 dp[m][n],表示最多由 m 个 0 和 n 个 1 组成的字符串数量
return dp[m][n];
}

// 辅助方法,计算字符串中 0 和 1 的数量
private int[] calcZeroAndOne(String str) {
int[] res = new int[2]; // 初始化结果数组,res[0] 表示 0 的数量,res[1] 表示 1 的数量
for (char c : str.toCharArray()) {
res[c - '0']++; // 统计字符 c 为 0 或 1 的数量
}
return res; // 返回结果数组
}
}