# 474. Ones and Zeroes
LeetCode Problem Link (opens new window)
Given a binary string array strs and two integers m and n.
Find the size of the largest subset of strs that contains at most m zeros and n ones.
A subset x is a subset of y if all elements of x are also elements of y.
Example 1:
Input:
strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3Output:
4Explanation: The largest subset that contains at most
5zeros and3ones is{"10","0001","1","0"}, so the answer is4. Other subsets that meet the conditions but are smaller include{"0001","1"}and{"10","1","0"}.{"111001"}does not meet the condition because it contains4ones, which is more than the allowed3.
Example 2:
- Input:
strs = ["10", "0", "1"], m = 1, n = 1 - Output:
2 - Explanation: The largest subset is
{"0", "1"}, so the answer is2.
Constraints:
1 <= strs.length <= 6001 <= strs[i].length <= 100strs[i]consists only of'0'and'1'.1 <= m, n <= 100
# Thought Process
If you're not familiar with the knapsack problem, first check out these two articles:
- Dynamic Programming: Knapsack Theory 01 Knapsack-1 (opens new window)
- Dynamic Programming: Knapsack Theory 01 Knapsack-2 (opens new window)
This problem can be a bit tricky, somewhat like a brain teaser programmers pose to themselves. Why make it harder for programmers?
About this problem, some may think of it as a multi-knapsack problem, and some explanations are written this way.
However, this problem is not a multi-knapsack problem. Let's review with this diagram to clarify the different types of knapsack problems:

The multiple knapsack problem deals with items that have different quantities.
In this problem, the elements in the strs array are the items, and each item represents one!
The values m and n are analogous to a knapsack, but with two dimensions.
Some might confuse m and n as different items, feeling it's a multiple knapsack problem.
But this problem is actually a 0-1 knapsack problem!
Although, this knapsack has two dimensions, one being m and the other being n. Different lengths of strings are items of varying sizes waiting to be packed.
Let's start the five-step dynamic programming process:
- Define the
dparray (dp table) and the meaning of the indices
dp[i][j]: The maximum size of the subset of strs with at most i zeros and j ones is dp[i][j].
- Establish the recurrence relation
dp[i][j] can be derived from the previous string in strs, each string containing zeroNum zeros, and oneNum ones.
dp[i][j] can be dp[i - zeroNum][j - oneNum] + 1.
During iteration, we take the maximum value of dp[i][j].
So the recurrence relation is: dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
At this point, recall the recurrence relation of the 0-1 knapsack: dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
You'll find that the zeroNum and oneNum of strings are like the weights of items (weight[i]), while the count of items itself is like the item's value (value[i]).
This is a classic 0-1 knapsack problem! Just with item weights having two dimensions.
- Initialize the
dparray
In Dynamic Programming: Knapsack Theory 01 Knapsack-2 (opens new window), we explained how initializing the 0-1 knapsack dp array to 0 suffices.
Since item values won't be negative, initializing with 0 ensures the dp[i][j] won't be overwritten.
- Determine the iteration order
In Dynamic Programming: Knapsack Theory 01 Knapsack-2 (opens new window), it's explained why the 0-1 knapsack iterates in this order: outer for loop iterating over items, inner for loop iterating over knapsack capacities, and iterating from back to front.
Thus, for this problem, items are strings in strs, and the knapsack capacities are the m and n described in the problem statement.
Here's the code:
for (string str : strs) { // iterate over items
int oneNum = 0, zeroNum = 0;
for (char c : str) {
if (c == '0') zeroNum++;
else oneNum++;
}
for (int i = m; i >= zeroNum; i--) { // iterate over the knapsack's capacity from back to front!
for (int j = n; j >= oneNum; j--) {
dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
Some may wonder if the order of these two for loops matters?
It doesn't; they're both a dimension of item weights, so starting with either is fine!
- Example walkthrough for the
dparray
Using the input ["10", "0001", "111001", "1", "0"], m = 3, n = 3 as an example:
The resulting dp array looks as follows:

The dynamic programming five-step analysis is now complete, and the C++ code is as follows:
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
vector<vector<int>> dp(m + 1, vector<int> (n + 1, 0)); // initialize to 0
for (string str : strs) { // iterate over items
int oneNum = 0, zeroNum = 0;
for (char c : str) {
if (c == '0') zeroNum++;
else oneNum++;
}
for (int i = m; i >= zeroNum; i--) { // iterate over the knapsack capacity from back to front!
for (int j = n; j >= oneNum; j--) {
dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
}
}
}
return dp[m][n];
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- Time complexity:
O(kmn), wherekis the length ofstrs - Space complexity:
O(mn)
C++: Version using three-dimensional array
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
int num_of_str = strs.size();
vector<vector<vector<int>>> dp(num_of_str, vector<vector<int>>(m + 1,vector<int>(n + 1, 0)));
/* dp[i][j][k] represents, if choosing items among strs[0] to strs[i] to form a subset,
what is the maximum size of this subset such that there are no more than m 0's and n 1's in this subset.
Each entry of dp[i][j][k] is initialized with 0
transition formula:
using x[i] to indicates the number of 0's in strs[i]
using y[i] to indicates the number of 1's in strs[i]
dp[i][j][k] = max(dp[i-1][j][k], dp[i-1][j - x[i]][k - y[i]] + 1)
*/
// num_of_zeros records the number of 0's for each str
// num_of_ones records the number of 1's for each str
// find the number of 0's and the number of 1's for each str in strs
vector<int> num_of_zeros;
vector<int> num_of_ones;
for (auto& str : strs){
int count_of_zero = 0;
int count_of_one = 0;
for (char &c : str){
if(c == '0') count_of_zero ++;
else count_of_one ++;
}
num_of_zeros.push_back(count_of_zero);
num_of_ones.push_back(count_of_one);
}
// num_of_zeros[0] indicates the number of 0's for str[0]
// num_of_ones[0] indiates the number of 1's for str[1]
// initialize the 1st plane of dp[i][j][k], i.e., dp[0][j][k]
// if num_of_zeros[0] > m or num_of_ones[0] > n, no need to further initialize dp[0][j][k],
// because they have been intialized to 0 previously
if(num_of_zeros[0] <= m && num_of_ones[0] <= n){
// for j < num_of_zeros[0] or k < num_of_ones[0], dp[0][j][k] = 0
for(int j = num_of_zeros[0]; j <= m; j++){
for(int k = num_of_ones[0]; k <= n; k++){
dp[0][j][k] = 1;
}
}
}
/* if j - num_of_zeros[i] >= 0 and k - num_of_ones[i] >= 0:
dp[i][j][k] = max(dp[i-1][j][k], dp[i-1][j - num_of_zeros[i]][k - num_of_ones[i]] + 1)
else:
dp[i][j][k] = dp[i-1][j][k]
*/
for (int i = 1; i < num_of_str; i++){
int count_of_zeros = num_of_zeros[i];
int count_of_ones = num_of_ones[i];
for (int j = 0; j <= m; j++){
for (int k = 0; k <= n; k++){
if( j < count_of_zeros || k < count_of_ones){
dp[i][j][k] = dp[i-1][j][k];
}else{
dp[i][j][k] = max(dp[i-1][j][k], dp[i-1][j - count_of_zeros][k - count_of_ones] + 1);
}
}
}
}
return dp[num_of_str-1][m][n];
}
};
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# Summary
Some might have encountered this problem and not realized what kind of knapsack it actually is.
This time, we've unlocked several applications of the 0-1 knapsack:
- Pure 0-1 Knapsack (opens new window) is to compute the maximum achievable value when filling a knapsack of given capacity.
- 416.Partition Equal Subset Sum-1 (opens new window) checks if the knapsack can be exactly filled with given capacity.
- 1049. Last Stone Weight II (opens new window) calculates how much can be packed with a given capacity.
- 494. Target Sum (opens new window) asks how many ways you can fill the knapsack with a given capacity.
- This problem asks for the maximum number of items in the knapsack given its capacity.
The examples in "Code Thinking Notes" showcase 0-1 knapsack applications across different dimensions, offering a chance to savor each one deeply!
# Versions in Other Programming Languages
# Java
Three-dimensional DP array implementation
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
/// The array has three dimensions
// The first dimension: choose from the first few strings
// The second dimension: limit of zeros (knapsack dimension 1)
// The third dimension: limit of ones (knapsack dimension 2)
int[][][] dpArr = new int[strs.length][m + 1][n + 1];
/// Initialize the dpArr array
// Calculate the number of zeros and ones in the first string
int zeroNum = 0;
int oneNum = 0;
for (char c : strs[0].toCharArray()) {
if (c == '0') {
zeroNum++;
} else {
oneNum++;
}
}
// When the number of zeros and ones fits the first string, initialize the corresponding positions of the DP array to 1, as the current subset count is 1
for (int j = zeroNum; j <= m; j++) {
for (int k = oneNum; k <= n; k++) {
dpArr[0][j][k] = 1;
}
}
/// Fill in the DP array after adding the i-th string
for (int i = 1; i < strs.length; i++) {
zeroNum = 0;
oneNum = 0;
for (char c : strs[i].toCharArray()) {
if (c == '0') {
zeroNum++;
} else {
oneNum++;
}
}
for (int j = 0; j <= m; j++) {
for (int k = 0; k <= n; k++) {
if (j >= zeroNum && k >= oneNum) {
// --if-- When both the zero dimension and the one dimension capacity are greater than or equal to the current string's zero and one count, consider whether to put the current string in the knapsack
// Not putting in the i-th string, the subset count remains dpArr[i - 1][j][k]
// Putting in the i-th string requires vacating zeroNum spaces in dimension 1 and oneNum spaces in dimension 2, then putting in the current string, i.e., dpArr[i - 1][j - zeroNum][k - oneNum] + 1)
dpArr[i][j][k] = Math.max(dpArr[i - 1][j][k], dpArr[i - 1][j - zeroNum][k - oneNum] + 1);
} else {
// --if-- Unable to put in the i-th string, the subset count remains dpArr[i - 1][j][k]
dpArr[i][j][k] = dpArr[i - 1][j][k];
}
}
}
}
return dpArr[dpArr.length - 1][m][n];
}
}
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
Two-dimensional DP array implementation
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
//dp[i][j] indicates the largest subset with i zeros and j ones
int[][] dp = new int[m + 1][n + 1];
int oneNum, zeroNum;
for (String str : strs) {
oneNum = 0;
zeroNum = 0;
for (char ch : str.toCharArray()) {
if (ch == '0') {
zeroNum++;
} else {
oneNum++;
}
}
// Iterate in reverse order
for (int i = m; i >= zeroNum; i--) {
for (int j = n; j >= oneNum; j--) {
dp[i][j] = Math.max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
}
}
}
return dp[m][n];
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Python
DP (Version 1)
class Solution:
def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
dp = [[0] * (n + 1) for _ in range(m + 1)] # Initialize 2D dynamic programming array to 0
for s in strs: # Iterate over items
zeroNum = s.count('0') # Count zeros
oneNum = len(s) - zeroNum # Count ones
for i in range(m, zeroNum - 1, -1): # Iterate over knapsack capacity from back to front
for j in range(n, oneNum - 1, -1):
dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1) # Recurrence relation
return dp[m][n]
2
3
4
5
6
7
8
9
10
11
DP (Version 2)
class Solution:
def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
dp = [[0] * (n + 1) for _ in range(m + 1)] # Initialize 2D dynamic programming array to 0
# Iterate over items
for s in strs:
ones = s.count('1') # Count ones in string
zeros = s.count('0') # Count zeros in string
# Iterate over knapsack capacity from back to front
for i in range(m, zeros - 1, -1):
for j in range(n, ones - 1, -1):
dp[i][j] = max(dp[i][j], dp[i - zeros][j - ones] + 1) # Recurrence relation
return dp[m][n]
2
3
4
5
6
7
8
9
10
11
12
13
# Go
func findMaxForm(strs []string, m int, n int) int {
// Define array
dp := make([][]int, m+1)
for i,_ := range dp {
dp[i] = make([]int, n+1 )
}
// Iterate
for i:=0;i<len(strs);i++ {
zeroNum,oneNum := 0 , 0
// Count 0s and 1s
// Or directly use strings.Count(strs[i],"0")
for _,v := range strs[i] {
if v == '0' {
zeroNum++
}
}
oneNum = len(strs[i])-zeroNum
// Iterate knapsack capacity in reverse
for j:= m ; j >= zeroNum;j-- {
for k:=n ; k >= oneNum;k-- {
// Recurrence formula
dp[j][k] = max(dp[j][k],dp[j-zeroNum][k-oneNum]+1)
}
}
//fmt.Println(dp)
}
return dp[m][n]
}
func max(a,b int) int {
if a > b {
return a
}
return b
}
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
# JavaScript
const findMaxForm = (strs, m, n) => {
const dp = Array.from(Array(m+1), () => Array(n+1).fill(0));
let numOfZeros, numOfOnes;
for(let str of strs) {
numOfZeros = 0;
numOfOnes = 0;
for(let c of str) {
if (c === '0') {
numOfZeros++;
} else {
numOfOnes++;
}
}
for(let i = m; i >= numOfZeros; i--) {
for(let j = n; j >= numOfOnes; j--) {
dp[i][j] = Math.max(dp[i][j], dp[i - numOfZeros][j - numOfOnes] + 1);
}
}
}
return dp[m][n];
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# TypeScript
Rolling array, two-dimensional array method
type BinaryInfo = { numOfZero: number, numOfOne: number };
function findMaxForm(strs: string[], m: number, n: number): number {
const goodsNum: number = strs.length;
const dp: number[][] = new Array(m + 1).fill(0)
.map(_ => new Array(n + 1).fill(0));
for (let i = 0; i < goodsNum; i++) {
const { numOfZero, numOfOne } = countBinary(strs[i]);
for (let j = m; j >= numOfZero; j--) {
for (let k = n; k >= numOfOne; k--) {
dp[j][k] = Math.max(dp[j][k], dp[j - numOfZero][k - numOfOne] + 1);
}
}
}
return dp[m][n];
};
function countBinary(str: string): BinaryInfo {
let numOfZero: number = 0,
numOfOne: number = 0;
for (let s of str) {
if (s === '0') {
numOfZero++;
} else {
numOfOne++;
}
}
return { numOfZero, numOfOne };
}
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
Traditional knapsack, three-dimensional array method
type BinaryInfo = { numOfZero: number, numOfOne: number };
function findMaxForm(strs: string[], m: number, n: number): number {
/**
dp[i][j][k]: front i-th item, 0 capacity of knapsack is j, 1 capacity is k, most items count
*/
const goodsNum: number = strs.length;
const dp: number[][][] = new Array(goodsNum).fill(0)
.map(_ => new Array(m + 1)
.fill(0)
.map(_ => new Array(n + 1).fill(0))
);
const { numOfZero, numOfOne } = countBinary(strs[0]);
for (let i = numOfZero; i <= m; i++) {
for (let j = numOfOne; j <= n; j++) {
dp[0][i][j] = 1;
}
}
for (let i = 1; i < goodsNum; i++) {
const { numOfZero, numOfOne } = countBinary(strs[i]);
for (let j = 0; j <= m; j++) {
for (let k = 0; k <= n; k++) {
if (j < numOfZero || k < numOfOne) {
dp[i][j][k] = dp[i - 1][j][k];
} else {
dp[i][j][k] = Math.max(dp[i - 1][j][k], dp[i - 1][j - numOfZero][k - numOfOne] + 1);
}
}
}
}
return dp[dp.length - 1][m][n];
};
function countBinary(str: string): BinaryInfo {
let numOfZero: number = 0,
numOfOne: number = 0;
for (let s of str) {
if (s === '0') {
numOfZero++;
} else {
numOfOne++;
}
}
return { numOfZero, numOfOne };
}
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
39
40
41
42
43
Backtracking method (will timeout)
function findMaxForm(strs: string[], m: number, n: number): number {
/**
Thought: Brutally enumerate all subsets of strs, record the maximum length of the subsets that meet the conditions
*/
let resMax: number = 0;
backTrack(strs, m, n, 0, []);
return resMax;
function backTrack(
strs: string[], m: number, n: number,
startIndex: number, route: string[]
): void {
if (startIndex === strs.length) return;
for (let i = startIndex, length = strs.length; i < length; i++) {
route.push(strs[i]);
if (isValidSubSet(route, m, n)) {
resMax = Math.max(resMax, route.length);
backTrack(strs, m, n, i + 1, route);
}
route.pop();
}
}
};
function isValidSubSet(strs: string[], m: number, n: number): boolean {
let zeroNum: number = 0,
oneNum: number = 0;
strs.forEach(str => {
for (let s of str) {
if (s === '0') {
zeroNum++;
} else {
oneNum++;
}
}
});
return zeroNum <= m && oneNum <= n;
}
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
# Scala
Knapsack:
object Solution {
def findMaxForm(strs: Array[String], m: Int, n: Int): Int = {
var dp = Array.ofDim[Int](m + 1, n + 1)
var (oneNum, zeroNum) = (0, 0)
for (str <- strs) {
oneNum = 0
zeroNum = 0
for (i <- str.indices) {
if (str(i) == '0') zeroNum += 1
else oneNum += 1
}
for (i <- m to zeroNum by -1) {
for (j <- n to oneNum by -1) {
dp(i)(j) = math.max(dp(i)(j), dp(i - zeroNum)(j - oneNum) + 1)
}
}
}
dp(m)(n)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Backtracking method (timed out):
object Solution {
import scala.collection.mutable
var res = Int.MinValue
def test(str: String): (Int, Int) = {
var (zero, one) = (0, 0)
for (i <- str.indices) {
if (str(i) == '1') one += 1
else zero += 1
}
(zero, one)
}
def travsel(strs: Array[String], path: mutable.ArrayBuffer[String], m: Int, n: Int, startIndex: Int): Unit = {
if (startIndex > strs.length) {
return
}
res = math.max(res, path.length)
for (i <- startIndex until strs.length) {
var (zero, one) = test(strs(i))
// If the number of zeros is less than m and the number of ones is less than n, then backtrack
if (zero <= m && one <= n) {
path.append(strs(i))
travsel(strs, path, m - zero, n - one, i + 1)
path.remove(path.length - 1)
}
}
}
def findMaxForm(strs: Array[String], m: Int, n: Int): Int = {
res = Int.MinValue
var path = mutable.ArrayBuffer[String]()
travsel(strs, path, m, n, 0)
res
}
}
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
39
40
41
# Rust
impl Solution {
pub fn find_max_form(strs: Vec<String>, m: i32, n: i32) -> i32 {
let (m, n) = (m as usize, n as usize);
let mut dp = vec![vec![0; n + 1]; m + 1];
for s in strs {
let (mut one_num, mut zero_num) = (0, 0);
for c in s.chars() {
match c {
'0' => zero_num += 1,
'1' => one_num += 1,
_ => (),
}
}
for i in (zero_num..=m).rev() {
for j in (one_num..=n).rev() {
dp[i][j] = dp[i][j].max(dp[i - zero_num][j - one_num] + 1);
}
}
}
dp[m][n]
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# C
#define max(a, b) ((a) > (b) ? (a) : (b))
int findMaxForm(char** strs, int strsSize, int m, int n) {
int dp[m + 1][n + 1];
memset(dp, 0, sizeof (int ) * (m + 1) * (n + 1));
for(int i = 0; i < strsSize; i++){
// Count zeros and ones
int count0 = 0;
int count1 = 0;
char *str = strs[i];
while (*str != '\0'){
if(*str == '0'){
count0++;
} else{
count1++;
}
str++;
}
for(int j = m; j >= count0; j--){
for(int k = n; k >= count1; k--){
dp[j][k] = max(dp[j][k], dp[j - count0][k - count1] + 1);
}
}
}
return dp[m][n];
}
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
# C#
public class Solution
{
public int FindMaxForm(string[] strs, int m, int n)
{
int[,] dp = new int[m + 1, n + 1];
foreach (string str in strs)
{
int zero = 0, one = 0;
foreach (char c in str)
{
if (c == '0') zero++;
else one++;
}
for (int i = m; i >= zero; i--)
{
for (int j = n; j >= one; j--)
{
dp[i, j] = Math.Max(dp[i, j], dp[i - zero, j - one] + 1);
}
}
}
return dp[m, n];
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24