Check if a matrix contains a square submatrix with 0 as boundary element

Given an N*N binary matrix arr[][], the task is to check if the matrix contains a square of at least size 2 x 2 whose boundaries are made up of only 0s.

Examples:

Input:
arr[][] = {
{1, 1, 1, 0, 1, 0},
{0, 0, 0, 0, 0, 1},
{0, 1, 1, 1, 0, 1},
{0, 0, 0, 1, 0, 1},
{0, 1, 1, 1, 0, 1},
{0, 0, 0, 0, 0, 1}
}
Output: True
Explanation:
Since, arr[][] contains square matrix with all 0’s at boundary, so answer is True.
{
{  ,  ,  ,  ,  , },
{0,   0,   0,   0,   0, },
{0,  ,  ,  ,   0, },
{0,  ,  ,  ,   0, },
{0,  ,  ,  ,  0, },
{0,   0,   0,   0,   0, }
}

Input:
arr[][] = {
{1, 1, 1, 0, 1, 0},
{0, 0, 0, 0, 0, 1},
{0, 1, 1, 1, 0, 1},
{0, 0, 0, 1, 1, 1},
{0, 1, 1, 1, 0, 1},
{0, 0, 0, 0, 0, 1}
}
Output: False
Explanation: There is no square in the matrix whose borders are made up of only 0s.

Approach:



  1. A square is defined by its topmost and bottommost rows and by its leftmost and rightmost columns.
  2. Given a pair of rows and a pair of columns that form a valid square, you can easily determine if the relevant square is a square of zeroes with two for loops.
  3. It is required to iterate over every valid square in the input matrix, arr[][].
  4. We can start iterating from the outermost square and recursively go inwards in the matrix.
  5. On moving inward, from (r1, c1) and (r2, c2) we have 5 options, which will generate square matrix:-
    a) (r1 + 1, c1 + 1), (r2 – 1, c2 – 1)
    b) (r1, c1 + 1), (r2 – 1, c2)
    c) (r1 + 1, c1), (r2, c2 – 1)
    d) (r1 + 1, c1 + 1), (r2, c2)
    e) (r1, c1), (r2 – 1, c2 – 1)
  6. Since, the problem has many overlapping sub-problem, so we need to use cache/memoization to avoid duplicate computations.

Below is the implementation of the above approach:

filter_none

edit
close

play_arrow

link
brightness_4
code

// C++ implementation of the above approach
  
#include <bits/stdc++.h>
using namespace std;
  
bool hasSquareOfZeroes(
    vector<vector<int> >& matrix,
    int r1, int c1, int r2, int c2,
    unordered_map<string, bool>& cache);
  
bool isSquareOfZeroes(
    vector<vector<int> >& matrix,
    int r1, int c1,
    int r2, int c2);
  
// Function checks if square
// with all 0's in boundary
// exists in the matrix
bool squareOfZeroes(
    vector<vector<int> > matrix)
{
    int lastIdx = matrix.size() - 1;
    unordered_map<string, bool> cache;
    return hasSquareOfZeroes(
        matrix,
        0, 0,
        lastIdx,
        lastIdx,
        cache);
}
  
// Function iterate inward in
// the matrix and checks the
// square obtained and memoize/cache
// the result to avoid duplicate computation
  
// r1 is the top row,
// c1 is the left col
// r2 is the bottom row,
// c2 is the right
bool hasSquareOfZeroes(
    vector<vector<int> >& matrix,
    int r1, int c1, int r2, int c2,
    unordered_map<string, bool>& cache)
{
    if (r1 >= r2 || c1 >= c2)
        return false;
    string key = to_string(r1) + '-'
                 + to_string(c1) + '-'
                 + to_string(r2) + '-'
                 + to_string(c2);
  
    if (cache.find(key) != cache.end())
        return cache[key];
  
    cache[key]
        = isSquareOfZeroes(
              matrix, r1, c1, r2, c2)
          || hasSquareOfZeroes(
                 matrix, r1 + 1, c1 + 1,
                 r2 - 1, c2 - 1, cache)
          || hasSquareOfZeroes(
                 matrix, r1, c1 + 1,
                 r2 - 1, c2, cache)
          || hasSquareOfZeroes(
                 matrix, r1 + 1, c1,
                 r2, c2 - 1, cache)
          || hasSquareOfZeroes(
                 matrix, r1 + 1, c1 + 1,
                 r2, c2, cache)
          || hasSquareOfZeroes(
                 matrix, r1, c1,
                 r2 - 1, c2 - 1, cache);
  
    return cache[key];
}
  
// Function checks if the
// boundary of the square
// consists of 0's
bool isSquareOfZeroes(
    vector<vector<int> >& matrix,
    int r1, int c1,
    int r2, int c2)
{
    for (int row = r1; row < r2 + 1; row++) {
        if (matrix[row][c1] != 0
            || matrix[row][c2] != 0)
            return false;
    }
    for (int col = c1; col < c2 + 1; col++) {
        if (matrix[r1][col] != 0
            || matrix[r2][col] != 0)
            return false;
    }
    return true;
}
  
// Driver Code
int main()
{
    vector<vector<int> > matrix{
        { 1, 1, 1, 0, 1, 0 },
        { 0, 0, 0, 0, 0, 1 },
        { 0, 1, 1, 1, 0, 1 },
        { 0, 0, 0, 1, 0, 1 },
        { 0, 1, 1, 1, 0, 1 },
        { 0, 0, 0, 0, 0, 1 }
    };
    int ans;
    ans = squareOfZeroes(matrix);
  
    if (ans == 1) {
        cout << "True" << endl;
    }
    else {
        cout << "False" << endl;
    }
}
chevron_right

Output:
True

Time Complexity: O(N^4)
Space Complexity: O(N^3)

Efficient Approach: In order to optimize the above approach, we need to follow the below steps:

  1. We need to precompute two values for every element in the matrix: the number of 0s to the right of each element (including the element itself) and the number of 0s below each element (including the element itself).
  2. We can compute these values by iterating through the matrix starting at the bottom right corner and moving way up by traversing each row from right to left.
  3. Once, we have computed the matrix, then we can check the boundary of square whether it is made up of all 0’s in constant time.
  4. To check the boundary of the square, we just need to look at the number of 0s below any square’s two top corners and the number of 0s to the right of the same square’s two left corners.
    1. Below is the implementation of the above approach:

      filter_none

      edit
      close

      play_arrow

      link
      brightness_4
      code

      // C++ implementation of the above approach
        
      #include <bits/stdc++.h>
      using namespace std;
        
      // Structure to store precomputed
      // value corresponding to each
      // element of the matrix
      struct InfoMatrixItem {
          int numZeroesBelow;
          int numZeroesRight;
      };
      bool isSquareOfZeroes(
          vector<vector<InfoMatrixItem> >
              infoMatrix,
          int r1, int c1,
          int r2, int c2);
        
      vector<vector<InfoMatrixItem> >
      preComputedNumOfZeroes(
          vector<vector<int> > matrix);
        
      bool squareOfZeroes(
          vector<vector<int> > matrix)
      {
          vector<vector<InfoMatrixItem> > infoMatrix
              = preComputedNumOfZeroes(matrix);
        
          int n = matrix.size();
        
          for (int topRow = 0;
               topRow < n;
               topRow++) {
        
              for (int leftCol = 0;
                   leftCol < n;
                   leftCol++) {
        
                  int squareLength = 2;
        
                  while (squareLength <= n - leftCol
                         && squareLength <= n - topRow) {
        
                      int bottomRow = topRow
                                      + squareLength - 1;
                      int rightCol = leftCol
                                     + squareLength - 1;
        
                      if (isSquareOfZeroes(
                              infoMatrix, topRow,
                              leftCol, bottomRow,
                              rightCol))
                          return true;
        
                      squareLength++;
                  }
              }
          }
        
          return false;
      }
        
      // r1 is the top row,
      // c1 is the left column
      // r2 is the bottom row,
      // c2 is the right column
      bool isSquareOfZeroes(
          vector<vector<InfoMatrixItem> >
              infoMatrix,
          int r1, int c1,
          int r2, int c2)
      {
        
          int squareLength = c2 - c1 + 1;
        
          bool hasTopBorder
              = infoMatrix[r1][c1].numZeroesRight
                >= squareLength;
          bool hasLeftBorder
              = infoMatrix[r1][c1].numZeroesBelow
                >= squareLength;
          bool hasBottomBorder
              = infoMatrix[r2][c1].numZeroesRight
                >= squareLength;
          bool hasRightBorder
              = infoMatrix[r1][c2].numZeroesBelow
                >= squareLength;
        
          return hasTopBorder
                 && hasLeftBorder
                 && hasBottomBorder
                 && hasRightBorder;
      }
        
      vector<vector<InfoMatrixItem> >
      preComputedNumOfZeroes(
          vector<vector<int> > matrix)
      {
        
          vector<vector<InfoMatrixItem> > infoMatrix;
        
          for (int i = 0; i < matrix.size(); i++) {
        
              vector<InfoMatrixItem> inner;
        
              for (int j = 0;
                   j < matrix[i].size();
                   j++) {
        
                  int numZeroes = matrix[i][j] == 0
                                      ? 1
                                      : 0;
        
                  inner.push_back(InfoMatrixItem{
                      numZeroes, numZeroes });
              }
        
              infoMatrix.push_back(inner);
          }
        
          int lastIdx = matrix.size() - 1;
        
          for (int row = lastIdx;
               row >= 0; row--) {
              for (int col = lastIdx;
                   col >= 0; col--) {
        
                  if (matrix[row][col] == 1)
                      continue;
        
                  if (row < lastIdx) {
                      infoMatrix[row][col]
                          .numZeroesBelow
                          += infoMatrix[row + 1]
                                       [col]
                                           .numZeroesBelow;
                  }
        
                  if (col < lastIdx) {
                      infoMatrix[row][col]
                          .numZeroesRight
                          += infoMatrix[row]
                                       [col + 1]
                                           .numZeroesRight;
                  }
              }
          }
          return infoMatrix;
      }
        
      // Driver Code
      int main()
      {
          vector<vector<int> > matrix{
              { 1, 1, 1, 0, 1, 0 },
              { 0, 0, 0, 0, 0, 1 },
              { 0, 1, 1, 1, 0, 1 },
              { 0, 0, 0, 1, 0, 1 },
              { 0, 1, 1, 1, 0, 1 },
              { 0, 0, 0, 0, 0, 1 }
          };
          int ans;
          ans = squareOfZeroes(matrix);
          if (ans == 1) {
              cout << "True" << endl;
          }
          else {
              cout << "False" << endl;
          }
      }
      chevron_right
      
      
      Output:
      True
      

      Time Complexity : O(N^3)
      Space Complexity : O(N^2)





      I am currently pursuing B Tech (Hons) in Computer Science I love technology and explore new things I want to become a very good programmer I love to travel and meet new peoples

      If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

      Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.


Article Tags :