Dynamic Programming · 56 of 60
Largest area rectangular sub-matrix with equal number of 1s and 0s
In a grid of 0s and 1s, find the biggest rectangle that has exactly as many 0s as 1s.
- Simple way
- time O(R^3 * C^3)
room O(1) - Clever way
- time O(C^2 * R)
room O(R)
The problem, simply
You get a grid with R rows and C columns. Every cell is 0 or 1. Find a rectangle of neighboring cells that holds the same number of 0s and 1s, and make its area as big as possible. Give back the area, or 0 if there is none.
A story to remember it
Picture it
A grid of 0s and 1s. The marked rectangle, the top two rows, has four 0s and four 1s. Its area is 8.
- answer
Examples
A normal case
- In
[[0, 0, 1, 1], [0, 1, 1, 0], [1, 1, 1, 0]]- Out
8
The top two rows have four 0s and four 1s.
A small square
- In
[[0, 1], [1, 0]]- Out
4
The whole grid has two of each.
Only 1s
- In
[[1, 1], [1, 1]]- Out
0
No 0s at all, so no rectangle can be balanced.
One row
- In
[[1, 0, 1, 1, 0, 0]]- Out
6
The whole row has three of each. A one row grid is the plain "longest subarray with equal 0s and 1s" problem.
Way 1: the simple way
Try every rectangle and count.
- Pick a top and bottom row, and a left and right column.
- Count the 0s and 1s inside.
- If the counts are equal, compare the area with the best.
class Solution {
public int largestEqualArea(int[][] grid) {
int R = grid.length, C = grid[0].length, best = 0;
for (int top = 0; top < R; top++)
for (int bottom = top; bottom < R; bottom++)
for (int left = 0; left < C; left++)
for (int right = left; right < C; right++) {
int ones = 0, zeros = 0;
for (int i = top; i <= bottom; i++)
for (int j = left; j <= right; j++) {
if (grid[i][j] == 1) ones++;
else zeros++;
}
if (ones == zeros) {
best = Math.max(best, (bottom - top + 1) * (right - left + 1));
}
}
return best;
}
}How long it takes: about R^2 * C^2 rectangles and up to R * C cells to count in each, O(R^3 * C^3). Extra room: two counters, O(1).
Way 2: the clever way
- Treat every 0 as -1 (you can do it on the fly, without copying the grid).
- Pick a left column. Keep
rowSum, one number per row, starting at 0. - Move the right column across. Add that column into
rowSum. - In
rowSum, find the longest run of rows that adds up to 0, using a running total and a hash map from each total to the first row it appeared (total 0 starts at row -1). - Run length times strip width is a balanced rectangle. Keep the biggest.
Turn 0 into -1, then hunt for zero total strips
01 / 16
First, change every 0 into -1. Now a rectangle with as many 0s as 1s is exactly a rectangle whose numbers add up to 0, because each -1 cancels a 1.
- answer
- path
- just added
import java.util.HashMap;
import java.util.Map;
class Solution {
public int largestEqualArea(int[][] grid) {
int R = grid.length, C = grid[0].length, best = 0;
for (int left = 0; left < C; left++) {
int[] rowSum = new int[R];
for (int right = left; right < C; right++) {
for (int i = 0; i < R; i++) {
rowSum[i] += grid[i][right] == 1 ? 1 : -1; // a 0 counts as -1
}
int rows = longestZeroRun(rowSum);
best = Math.max(best, rows * (right - left + 1));
}
}
return best;
}
private int longestZeroRun(int[] arr) {
Map<Integer, Integer> first = new HashMap<>(); // running total -> first row
first.put(0, -1);
int total = 0, best = 0;
for (int i = 0; i < arr.length; i++) {
total += arr[i];
if (first.containsKey(total)) best = Math.max(best, i - first.get(total));
else first.put(total, i);
}
return best;
}
}How long it takes: C^2 / 2 strips, O(R) work each, O(C^2 * R). Choose strips along the shorter side to get O(min(R, C)^2 * max(R, C)). Extra room: rowSum and the map, O(R).
Since running totals in one strip stay between -R * width and R * width, you can swap the hash map for an array with an offset. It is faster in practice but the same big O.
Watch out for
- Put total 0 at row -1 in the map before the scan, or runs that start at the top row are missed.
- Keep the first row for each total. Overwriting it makes runs shorter.
- The answer is an area: rows times columns, not just rows.
- A balanced rectangle always has an even area. If you get an odd answer, something is off.
What interviewers ask next
- The one row version: longest subarray with equal 0s and 1s. It is the
longestZeroRunstep alone,O(n). - Largest rectangle with sum 0 in any integer grid: exactly this algorithm without the -1 step.
- Largest rectangle of only 1s: a different trick. Build bar heights row by row and use the largest rectangle in a histogram with a stack,
O(R * C). - Count balanced rectangles instead of finding the largest: in the 1D step, add how many times the current total was seen before.
- Equal numbers of three kinds (0, 1, 2): track two differences, like (count1 - count0, count2 - count0), as the map key.
Remember this
Grown-up words: the minus sign trick is a transformation to a zero sum problem, squashing columns is column compression, the running total is a prefix sum, and the notebook is a hash map from prefix sum to first index.