Welcome to Subscribe On Youtube

Question

Formatted question description: https://leetcode.ca/all/302.html

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.

Algorithm

Key point:

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.

For binary search implementation

When implementing binary search, the choice between using while start < end or while start <= end depends on the specific problem and the range of values being searched.

  • Use while start < end when you want to continue the search until the range between start and end is narrowed down to a single value. This is the most common scenario in binary search.
  • Use while start <= end when you want to include the possibility of the target value being at either start or end. This is typically used when searching for a specific target value in a range that is inclusive.

After the termination of the binary search loop, both start and end will be at the same position, representing the point where the target value is expected to be. It’s generally safe to return either start or end in most cases, as they should be pointing to the same value.

However, it’s important to consider the problem requirements and the specific implementation details. Sometimes, the problem statement or the specific algorithm may require returning start or end specifically. Make sure to carefully read the problem description or consider any specific requirements when determining which variable to return.

Code

  • 
    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<int[]> queue = new LinkedList<int[]>();
                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);
        }
    }
    
  • // OJ: https://leetcode.com/problems/smallest-rectangle-enclosing-black-pixels/
    // Time: O(MlogN + NlogM)
    // Space: O(M + N)
    class Solution {
    public:
        int minArea(vector<vector<char>>& A, int x, int y) {
            int M = A.size(), N = A[0].size();
            auto getRowLength = [&]() {
                int L = 0, R = x; // search min x
                while (L <= R) {
                    int mid = (L + R) / 2, j = 0;
                    while (j < N && A[mid][j] == '0') ++j;
                    if (j < N) R = mid - 1;
                    else L = mid + 1;
                }
                int mn = L; // minX = L
                L = x, R = M - 1;
                while (L <= R) {
                    int mid = (L + R) / 2, j = 0;
                    while (j < N && A[mid][j] == '0') ++j;
                    if (j < N) L = mid + 1;
                    else R = mid - 1;
                }
                return L - mn; // maxX = R = L - 1
            };
            auto getColumnLength = [&]() {
                int L = 0, R = y; // search min y
                while (L <= R) {
                    int mid = (L + R) / 2, i = 0;
                    while (i < M && A[i][mid] == '0') ++i;
                    if (i < M) R = mid - 1;
                    else L = mid + 1;
                }
                int mn = L; // minY = L
                L = y, R = N - 1;
                while (L <= R) {
                    int mid = (L + R) / 2, i = 0;
                    while (i < M && A[i][mid] == '0') ++i;
                    if (i < M) L = mid + 1;
                    else R = mid - 1;
                }
                return L - mn; // maxX = R = L - 1
            };
            return getRowLength() * getColumnLength();
        }
    };
    
  • '''
    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)
            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)
    }
    

All Problems

All Solutions