Skip to content

Commit ec042a5

Browse files
committed
feat(algorithms, dynamic-programming): maximal square
1 parent d7d14fc commit ec042a5

File tree

10 files changed

+112
-0
lines changed

10 files changed

+112
-0
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Maximal Square
2+
3+
Given an m x n binary matrix filled with 0's and 1's, find the largest square containing only 1's and return its area.
4+
5+
## Examples
6+
7+
```text
8+
matrix = [
9+
[0, 0, 1, 0, 0],
10+
[1, 1, 1, 0, 1],
11+
[0, 1, 1, 0, 0]
12+
]
13+
14+
Output: 4
15+
```
16+
17+
## Solution
18+
19+
The solution uses bottom-up dynamic programming to solve the problem. The solution is based on the observation that the
20+
size of the largest square ending (bottom-right corner) at a particular cell is equal to the minimum of the sizes of the
21+
largest squares ending at the three adjacent cells plus 1.
22+
23+
We create a 2D integer array dp of size (r + 1) x (c + 1) where r is the number of rows in the input array and c is the
24+
size of each row. dp[i][j] stores the side length of the largest square ending at the cell matrix[i - 1][j - 1]. All
25+
elements of dp are initialized to 0.
26+
27+
![Solution 1](./images/solutions/maximal_square_solution_1.png)
28+
29+
We then use a nested loop to iterate over the input array. For each cell matrix[i - 1][j - 1], we check if the cell
30+
contains a 1. If it does, we update dp[i][j] to the minimum of the sizes of the largest squares ending at the three
31+
adjacent cells plus 1. We also update a variable max_side to store the maximum side length of the largest square we
32+
have found so far.
33+
34+
![Solution 2](./images/solutions/maximal_square_solution_2.png)
35+
![Solution 3](./images/solutions/maximal_square_solution_3.png)
36+
![Solution 4](./images/solutions/maximal_square_solution_4.png)
37+
![Solution 5](./images/solutions/maximal_square_solution_5.png)
38+
![Solution 6](./images/solutions/maximal_square_solution_6.png)
39+
![Solution 7](./images/solutions/maximal_square_solution_7.png)
40+
41+
At the end of the loop, max_side contains the side length of the largest square containing only 1's in the input array.
42+
The area of the square is max_side * max_side.
43+
44+
### Complexity analysis
45+
46+
#### Time Complexity
47+
48+
O(m * n) where m is the number of rows and n is the number of columns in the input array. We iterate over each cell once,
49+
and for each cell, we perform a constant amount of work.
50+
51+
#### Space Complexity
52+
53+
O(m * n) where m is the number of rows and n is the number of columns. We use a 2D array dp of size (m + 1) x (n + 1) to
54+
store the side length of the largest square ending at each cell.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from typing import List
2+
3+
4+
def maximal_square(matrix: List[List[int]]) -> int:
5+
"""
6+
Returns the maximum area of a square containing only 1s in the 2D matrix
7+
Args:
8+
matrix(list): 2D matrix with 0s and 1s
9+
Returns:
10+
int: largest area of square with only 1s
11+
"""
12+
if not matrix:
13+
return 0
14+
15+
rows = len(matrix)
16+
cols = len(matrix[0])
17+
dp = [[0] * (cols + 1) for _ in range(rows + 1)]
18+
max_side = 0
19+
20+
for i in range(1, rows + 1):
21+
for j in range(1, cols + 1):
22+
if matrix[i - 1][j - 1] == 1:
23+
top = dp[i - 1][j]
24+
left = dp[i][j - 1]
25+
diag = dp[i - 1][j - 1]
26+
dp[i][j] = min(top, left, diag) + 1
27+
max_side = max(max_side, dp[i][j])
28+
29+
return max_side * max_side
74.2 KB
Loading
67.2 KB
Loading
75.7 KB
Loading
72.7 KB
Loading
66.3 KB
Loading
70.1 KB
Loading
68.1 KB
Loading
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import unittest
2+
from typing import List
3+
from parameterized import parameterized
4+
from algorithms.dynamic_programming.maximal_square import maximal_square
5+
6+
MAXIMAL_SQUARE_TEST_CASES = [
7+
(
8+
[
9+
[1, 0, 1, 0, 0],
10+
[1, 0, 1, 1, 1],
11+
[1, 1, 1, 1, 1],
12+
[1, 0, 0, 1, 0],
13+
],
14+
4,
15+
),
16+
([[0, 1], [1, 0]], 1),
17+
([[0]], 0),
18+
]
19+
20+
21+
class MaximalSquareTestCase(unittest.TestCase):
22+
@parameterized.expand(MAXIMAL_SQUARE_TEST_CASES)
23+
def test_maximal_square(self, matrix: List[List[int]], expected: int):
24+
actual = maximal_square(matrix)
25+
self.assertEqual(expected, actual)
26+
27+
28+
if __name__ == "__main__":
29+
unittest.main()

0 commit comments

Comments
 (0)