# 302. Smallest Rectangle Enclosing Black Pixels

## Description

You are given an m x n binary matrix image where 0 represents a white pixel and 1 represents a black pixel.

The black pixels are connected (i.e., there is only one black region). Pixels are connected horizontally and vertically.

Given two integers x and y that represents the location of one of the black pixels, return the area of the smallest (axis-aligned) rectangle that encloses all black pixels.

You must write an algorithm with less than O(mn) runtime complexity

Example 1:

Input: image = [["0","0","1","0"],["0","1","1","0"],["0","1","0","0"]], x = 0, y = 2
Output: 6


Example 2:

Input: image = [["1"]], x = 0, y = 0
Output: 1


Constraints:

• m == image.length
• n == image[i].length
• 1 <= m, n <= 100
• image[i][j] is either '0' or '1'.
• 0 <= x < m
• 0 <= y < n
• image[x][y] == '1'.
• The black pixels in the image only form one component.

## Solutions

Binary search.

Key point in question description:

there is only one black region.

So, no need to worry about 1s spread across the matrix.

Brute force: Traverse the entire array, if it encounters 1, update the rectangle.

Improvement, with a given black pixel (x, y) as the center, use the binary search to quickly find the upper, lower, left, and right critical points of the entire black area, and then directly calculate the area.

• public class Smallest_Rectangle_Enclosing_Black_Pixels {

public class Solution_BinarySearch {
/**
* @param image: a binary matrix with '0' and '1'
* @param x: the location of one of the black pixels
* @param y: the location of one of the black pixels
* @return: an integer
*/
public int minArea(char[][] image, int x, int y) {
if (image == null || image.length == 0) {
return 0;
}

int m = image.length;
int n = image[0].length;

int up = binarySearch(image, true, 0, x, 0, n, true);
int down = binarySearch(image, true, x + 1, m, 0, n, false);
int left = binarySearch(image, false, 0, y, up, down, true); // @note: re-use up/down to narrow down
int right = binarySearch(image, false, y + 1, n, up, down, false);

return (right - left) * (down - up);
}

int binarySearch(char[][] image, boolean isHorizontal, int i, int j, int low, int high, boolean opt) {

while (i < j) {
int k = low;
int mid = (i + j) / 2;

while (k < high && (isHorizontal ? image[mid][k] : image[k][mid]) == '0') {
++k;
}

if (k < high == opt) {
j = mid;
} else {
i = mid + 1;
}
}

return i;
}

}

class Solution_BruteForce {
public int minArea(char[][] image, int x, int y) {
int rows = image.length, columns = image[0].length;
int top = x, bottom = x, left = y, right = y;
int[][] directions = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };
image[x][y] = '2';
queue.offer(new int[]{x, y});
while (!queue.isEmpty()) {
int[] cell = queue.poll();
int row = cell[0], column = cell[1];
for (int[] direction : directions) {
int newRow = row + direction[0], newColumn = column + direction[1];
if (newRow >= 0 && newRow < rows && newColumn >= 0 && newColumn < columns && image[newRow][newColumn] == '1') {
image[newRow][newColumn] = '2';
top = Math.min(top, newRow);
bottom = Math.max(bottom, newRow);
left = Math.min(left, newColumn);
right = Math.max(right, newColumn);
queue.offer(new int[]{newRow, newColumn});
}
}
}
return (bottom - top + 1) * (right - left + 1);
}
}

}

//////

class Solution {
public int minArea(char[][] image, int x, int y) {
int m = image.length, n = image[0].length;
int left = 0, right = x;
while (left < right) {
int mid = (left + right) >> 1;
int c = 0;
while (c < n && image[mid][c] == '0') {
++c;
}
if (c < n) {
right = mid;
} else {
left = mid + 1;
}
}
int u = left;
left = x;
right = m - 1;
while (left < right) {
int mid = (left + right + 1) >> 1;
int c = 0;
while (c < n && image[mid][c] == '0') {
++c;
}
if (c < n) {
left = mid;
} else {
right = mid - 1;
}
}
int d = left;
left = 0;
right = y;
while (left < right) {
int mid = (left + right) >> 1;
int r = 0;
while (r < m && image[r][mid] == '0') {
++r;
}
if (r < m) {
right = mid;
} else {
left = mid + 1;
}
}
int l = left;
left = y;
right = n - 1;
while (left < right) {
int mid = (left + right + 1) >> 1;
int r = 0;
while (r < m && image[r][mid] == '0') {
++r;
}
if (r < m) {
left = mid;
} else {
right = mid - 1;
}
}
int r = left;
return (d - u + 1) * (r - l + 1);
}
}

• class Solution {
public:
int minArea(vector<vector<char>>& image, int x, int y) {
int m = image.size(), n = image[0].size();
int left = 0, right = x;
while (left < right) {
int mid = (left + right) >> 1;
int c = 0;
while (c < n && image[mid][c] == '0') ++c;
if (c < n)
right = mid;
else
left = mid + 1;
}
int u = left;
left = x;
right = m - 1;
while (left < right) {
int mid = (left + right + 1) >> 1;
int c = 0;
while (c < n && image[mid][c] == '0') ++c;
if (c < n)
left = mid;
else
right = mid - 1;
}
int d = left;
left = 0;
right = y;
while (left < right) {
int mid = (left + right) >> 1;
int r = 0;
while (r < m && image[r][mid] == '0') ++r;
if (r < m)
right = mid;
else
left = mid + 1;
}
int l = left;
left = y;
right = n - 1;
while (left < right) {
int mid = (left + right + 1) >> 1;
int r = 0;
while (r < m && image[r][mid] == '0') ++r;
if (r < m)
left = mid;
else
right = mid - 1;
}
int r = left;
return (d - u + 1) * (r - l + 1);
}
};

• '''
Below solution uses a binary search to find the left, right, top, and bottom edges of the rectangle.
We define two helper functions check_row and check_col to
determine if a given row or column contains any black pixels.
We use these functions to perform binary searches along the rows
and columns of the image to find the left, right, top, and bottom edges of the rectangle.
Finally, we calculate the area of the rectangle and return it.
'''

from typing import Callable

class Solution:
def minArea(self, image: List[List[str]], x: int, y: int) -> int:
m, n = len(image), len(image[0])
left = self.search(image, 0, y, self.check_col)
# 'not' for lambda is to toggle the mid moving direction,
#       basically switch if-else condition
right = self.search(image, y + 1, n, lambda col: not self.check_col(image, col))
top = self.search(image, 0, x, self.check_row)
bottom = self.search(image, x + 1, m, lambda row: not self.check_row(image, row))
return (right - left) * (bottom - top)

# unified search api
def search(self, image: List[List[str]], start: int, end: int, check: Callable) -> int:
while start < end:
mid = start + (end - start) // 2
if check(image, mid):
end = mid
else:
start = mid + 1
return start

def check_row(self, image: List[List[str]], row: int) -> bool:
return '1' in image[row]

def check_col(self, image: List[List[str]], col: int) -> bool:
return any(row[col] == '1' for row in image)

############

class Solution(object):
def minArea(self, image, x, y):
top = self.searchRows(image, 0, x, True) # most top row with 1s
bottom = self.searchRows(image, x + 1, len(image), False) # most bottom row with 1s
left = self.searchCols(image, 0, y, top, bottom, True)
right = self.searchCols(image, y + 1, len(image[0]), top, bottom, False)
return (right - left) * (bottom - top)

def searchRows(self, image, i, j, opt):
while i < j:
mid = i + (j - i) / 2
if ("1" in image[mid]) == opt:
j = mid
else:
i = mid + 1
return j

def searchCols(self, image, i, j, top, bottom, opt):
while i < j:
mid = i + (j - i) / 2
# if transpos image, then still can use above one: if ("1" in image[mid]) == opt:
if any([image[k][mid] == "1" for k in range(top, bottom)]) == opt:
j = mid
else:
i = mid + 1
return j

############

class Solution_BinarySearch:
def minArea(self, image: List[List[str]], x: int, y: int) -> int:
if not image:
return 0

m, n = len(image), len(image[0])

up = self.binarySearch(image, True, 0, x, 0, n, True)
down = self.binarySearch(image, True, x + 1, m, 0, n, False)
left = self.binarySearch(image, False, 0, y, up, down, True)
right = self.binarySearch(image, False, y + 1, n, up, down, False)

return (right - left) * (down - up)

def binarySearch(self, image: List[List[str]], isHorizontal: bool, i: int, j: int, low: int, high: int, opt: bool) -> int:
while i < j:
k = low
mid = (i + j) // 2

while k < high and (image[mid][k] if isHorizontal else image[k][mid]) == '0':
k += 1

if k < high == opt:
j = mid
else:
i = mid + 1

return i

############

class Solution:
def minArea(self, image: List[List[str]], x: int, y: int) -> int:
m, n = len(image), len(image[0])
left, right = 0, x
while left < right:
mid = (left + right) >> 1
c = 0
while c < n and image[mid][c] == '0':
c += 1
if c < n:
right = mid
else:
left = mid + 1
u = left
left, right = x, m - 1
while left < right:
mid = (left + right + 1) >> 1
c = 0
while c < n and image[mid][c] == '0':
c += 1
if c < n:
left = mid
else:
right = mid - 1
d = left
left, right = 0, y
while left < right:
mid = (left + right) >> 1
r = 0
while r < m and image[r][mid] == '0':
r += 1
if r < m:
right = mid
else:
left = mid + 1
l = left
left, right = y, n - 1
while left < right:
mid = (left + right + 1) >> 1
r = 0
while r < m and image[r][mid] == '0':
r += 1
if r < m:
left = mid
else:
right = mid - 1
r = left
return (d - u + 1) * (r - l + 1)


• func minArea(image [][]byte, x int, y int) int {
m, n := len(image), len(image[0])
left, right := 0, x
for left < right {
mid := (left + right) >> 1
c := 0
for c < n && image[mid][c] == '0' {
c++
}
if c < n {
right = mid
} else {
left = mid + 1
}
}
u := left
left, right = x, m-1
for left < right {
mid := (left + right + 1) >> 1
c := 0
for c < n && image[mid][c] == '0' {
c++
}
if c < n {
left = mid
} else {
right = mid - 1
}
}
d := left
left, right = 0, y
for left < right {
mid := (left + right) >> 1
r := 0
for r < m && image[r][mid] == '0' {
r++
}
if r < m {
right = mid
} else {
left = mid + 1
}
}
l := left
left, right = y, n-1
for left < right {
mid := (left + right + 1) >> 1
r := 0
for r < m && image[r][mid] == '0' {
r++
}
if r < m {
left = mid
} else {
right = mid - 1
}
}
r := left
return (d - u + 1) * (r - l + 1)
}