The O(n) Club: Ones and Zeroes — Binary Knapsack and the Joy of Resource Regret
⚡ TL;DR
Your mission: select the most binary strings possible from a pile, without blowing your quota of zeros (m) and ones (n). Brute-forcing every subset is an O(2^N) invitation to computer hell. Use dynamic programming and pack like a savvy Tetris champion instead:
// Java: See full code below for glory for (String s : strs) { int zeros = countChar(s, '0'); int ones = s.length() - zeros; for (int i = m; i >= zeros; i--) { for (int j = n; j >= ones; j--) { dp[i][j] = Math.max(dp[i][j], dp[i - zeros][j - ones] + 1); } } }
🧠 How Devs Usually Mess This Up
Have you ever let recursion fly and watched your CPU sob? Most mortals:
🔍 What’s Actually Going On
Meet the 0/1 Knapsack in binary pajamas. Each string s can only be picked at most once, and “weights” are now the count of zeros and ones it contains. Our goal: maximize how many strings you jam into your ~compute backpack~ without exceeding CPU (zeros) and RAM (ones) limits.
Imagine prepping for a retro hackathon. Each piece of gear—the binary strings—consumes both CPU and memory. Pick as much swag as you can… until your power supply is seconds from smoke. We solve this via a 2D DP table: dp[i][j]
holds, for i
zeros and j
ones remaining, the maximum subset size you can still achieve.
The crucial bit: iterate backwards for both constraints to avoid letting the same string sneak in twice. You’re not Doctor Strange—no time reversal nonsense here.
🛠️ PseudoCode
- Initialize: Make a
dp[m+1][n+1]
array, filled with zero. Everyone starts empty-handed. - For each binary string:
- Count zeros (
zeros
) and ones (ones
).
- Count zeros (
- Update backwards:
- For
i = m
down tozeros
andj = n
down toones
: - If you can “afford” the string, set
dp[i][j] = max(dp[i][j], dp[i-zeros][j-ones] + 1);
If you loop forwards, congrats—your code is now duplicating strings. (Cut that out!) - For
- Done?
dp[m][n]
is the answer: the best subset you can squeeze into your cloud instance/brain/truck before something explodes.
💻 The Code
public int findMaxForm(String[] strs, int m, int n) {
int[][] dp = new int[m + 1][n + 1];
for (String s : strs) {
int zeros = countChar(s, '0');
int ones = s.length() - zeros;
for (int i = m; i >= zeros; i--) {
for (int j = n; j >= ones; j--) {
dp[i][j] = Math.max(dp[i][j], dp[i - zeros][j - ones] + 1);
}
}
}
return dp[m][n];
}
private int countChar(String s, char c) {
int count = 0;
for (char ch : s.toCharArray()) {
if (ch == c) count++;
}
return count;
}
⚠️ Pitfalls, Traps & Runtime Smacks
- Subset isn’t subsequence: Just pick any combo—it’s not a memory game.
- Always loop DP indices backwards: Forward = duplicates. Programmer tears will be shed.
- Edge checks: What if the array is empty, or m/n are zero? Chill. Output is zero, as it should be.
- Complexity: O(L*m*n) time and O(m*n) space (where L is string count). So, happily efficient and RAM-friendly.
🧠 Interviewer Brain-Tattoo
“If your solution requires a 3D DP array, you’re not solving Ones and Zeroes—you’re auditioning for Java: Memory Glutton Edition.”