Maximal Square
Problem Statement
You are given a 2D matrix mat
only consisting of 0’s and 1’s.
1 0 0
0 1 1
1 1 1
We want to return d
where d
is the largest d
by d
matrix of 1’s in
mat
. In this example, we would return 2 as the largest square matrix we can
make is a 2 by 2 matrix of ones using the bottom right corner. Come up with an
efficient solution that returns the maximum value of d
.
Please also provide runtime and space complexity analysis. Source: Maximal Square
Starter Code
def maximal_square(mat):
"""
input mat: a binary matrix
output: an integer representing the dimensions of the largest square matrix of mat
"""
# your solution here
Test Cases
Test 1
Input:
1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
Output: 2
Test 2
Input:
1 0 1 0 0 1
1 1 1 1 1 1
1 1 1 0 1 1
1 1 1 1 0 1
Output: 3
Test 3
Input:
1 0
1 1
Output: 1
Hint
Hint 1 (Click to reveal!)
- How to approach this problem?
Let us simplify our approach by only considering the largest square at a position where the position represents the bottom right corner of our square
Hint 2 (Click to reveal!)
- Identifying a Pattern
If you know the largest square you can create around a given position in the matrix, what allows us to say we can create a valid subsquare at that position.
Solution
Click Here!
Algorithm
Naive Implementation
The most straightforward solution would be to try every single possible size that our square could be and then scan our matrix if a subsquare of that size is in our matrix. In the worst case, this yields a runtime of because in the worst case, we will traverse the matrix in its entirety times.
Using Dynammic Programming
Instead, we can consider a dynamic programming approach to this problem.
Let us initialize an n
by m
matrix with the same dimensions as mat
. We can
let each entry of the matrix represent the side length of the largest subsquare whose
bottom right corner is the current entry. Now, notice the value of an entry depends on
the values of the squares immediate above, left, and right of it. More specifically,
if corresponding entry in mat
is a 1
, then the largest subsquare we can create
is 1 plus the minimum of the largest subsquare we can create with the entries above,
right, and left of the entry.
Finally, to reduce our space complexity, since each entry only depends on the values of a single row at a time, we only need to keep track of a two rows at a time rather than the entire matrix.
Code
def maximal_square(mat):
"""
input mat: a binary matrix
output: an integer representing the dimensions of the largest square matrix of mat
"""
if rows == 0:
return 0
rows = len(matrix)
cols = len(matrix[0])
# Initialize our bottom-up dp matrix where dp[0] is the previous row and
# dp[1] is the current row
dp = [[0 for i in range(cols)] for j in range(2)]
d = 0
for row in range(rows):
for col in range(cols):
# Base case - if we're at the left or top of matrix
if row == 0 or col == 0:
dp[1][col] = mat[row][col]
else:
# If the current entry has a 1, find the largest possible square
# given the largest possible squares of the squares around it
if mat[row][col] == 1:
dp[1][col] = 1 + min(dp[0][col], dp[1][col - 1], dp[0][col - 1])
else:
dp[1][col] = 0
# Update the largest square we're found so far
d = max(d, dp[1][col])
# Move dp[1], our current row, to dp[0] the prev row
dp[0], dp[1] = dp[1], dp[0]
return d
Example
Below is an example of the full dp matrix (Note: in our implementation we only keep track of 2 rows at a time). Again, notice how every square is 1 plus the minimum of its upper left neighbors.
Input:
1 0 1 0 0 1
1 1 1 1 1 1
1 1 1 0 1 1
1 1 1 1 0 1
DP:
1 0 1 0 0 1
1 1 1 1 1 1
1 2 2 0 1 2
1 2 3 1 0 1
Runtime Analysis
The runtime of this algorithm is since we iterate through the matrix once.
Space Complexity
Our space complexity is since we are only keeping track of a constant number of rows at a time.
Follow-up Question
What if you wanted to find the largest d
by d
by d
cube in a rectangular prism?
Follow-up Solution
Solution for the Follow-up Question
We want to use the same concept as we did for original problem, but extended to 3 dimensions. Instead of storing the largest square we can make using that square as the bottom right square, we’ll instead store the largest cube we can create given that the entry is the bottom right corner closest to us. In addition to that we also need to consider not only the squares above and left of us, we also need to minimize over the square behind the entry. We need to tweak 3 things to our original solution for it to work in 3 dimensions. First, we need to adjust the base case to account for 3 dimensions. Second, we need to allow our dp matrix to hold planes, or 2-dimensional matrices, at a time rather than just rows. Finally, we need to alter our min function to include one extra dimension.