|
| 1 | +""" |
| 2 | +Question: |
| 3 | +Given a binary matrix mat of size n * m, find out the maximum size square |
| 4 | +sub-matrix with all 1s. |
| 5 | +
|
| 6 | +--- |
| 7 | +Example 1: |
| 8 | +
|
| 9 | +Input: |
| 10 | +n = 2, m = 2 |
| 11 | +mat = [[1, 1], |
| 12 | + [1, 1]] |
| 13 | +
|
| 14 | +Output: |
| 15 | +2 |
| 16 | +
|
| 17 | +Explanation: The maximum size of the square |
| 18 | +sub-matrix is 2. The matrix itself is the |
| 19 | +maximum sized sub-matrix in this case. |
| 20 | +--- |
| 21 | +Example 2 |
| 22 | +
|
| 23 | +Input: |
| 24 | +n = 2, m = 2 |
| 25 | +mat = [[0, 0], |
| 26 | + [0, 0]] |
| 27 | +Output: 0 |
| 28 | +
|
| 29 | +Explanation: There is no 1 in the matrix. |
| 30 | +
|
| 31 | +
|
| 32 | +Approach: |
| 33 | +We initialize another matrix (dp) with the same dimensions |
| 34 | +as the original one initialized with all 0’s. |
| 35 | +
|
| 36 | +dp_array(i,j) represents the side length of the maximum square whose |
| 37 | +bottom right corner is the cell with index (i,j) in the original matrix. |
| 38 | +
|
| 39 | +Starting from index (0,0), for every 1 found in the original matrix, |
| 40 | +we update the value of the current element as |
| 41 | +
|
| 42 | +dp_array(i,j)=dp_array(dp(i−1,j),dp_array(i−1,j−1),dp_array(i,j−1)) + 1. |
| 43 | +""" |
| 44 | + |
| 45 | + |
| 46 | +def largest_square_area_in_matrix_top_down_approch( |
| 47 | + rows: int, cols: int, mat: list[list[int]] |
| 48 | +) -> int: |
| 49 | + """ |
| 50 | + Function updates the largest_square_area[0], if recursive call found |
| 51 | + square with maximum area. |
| 52 | +
|
| 53 | + We aren't using dp_array here, so the time complexity would be exponential. |
| 54 | +
|
| 55 | + >>> largest_square_area_in_matrix_top_down_approch(2, 2, [[1,1], [1,1]]) |
| 56 | + 2 |
| 57 | + >>> largest_square_area_in_matrix_top_down_approch(2, 2, [[0,0], [0,0]]) |
| 58 | + 0 |
| 59 | + """ |
| 60 | + |
| 61 | + def update_area_of_max_square(row: int, col: int) -> int: |
| 62 | + |
| 63 | + # BASE CASE |
| 64 | + if row >= rows or col >= cols: |
| 65 | + return 0 |
| 66 | + |
| 67 | + right = update_area_of_max_square(row, col + 1) |
| 68 | + diagonal = update_area_of_max_square(row + 1, col + 1) |
| 69 | + down = update_area_of_max_square(row + 1, col) |
| 70 | + |
| 71 | + if mat[row][col]: |
| 72 | + sub_problem_sol = 1 + min([right, diagonal, down]) |
| 73 | + largest_square_area[0] = max(largest_square_area[0], sub_problem_sol) |
| 74 | + return sub_problem_sol |
| 75 | + else: |
| 76 | + return 0 |
| 77 | + |
| 78 | + largest_square_area = [0] |
| 79 | + update_area_of_max_square(0, 0) |
| 80 | + return largest_square_area[0] |
| 81 | + |
| 82 | + |
| 83 | +def largest_square_area_in_matrix_top_down_approch_with_dp( |
| 84 | + rows: int, cols: int, mat: list[list[int]] |
| 85 | +) -> int: |
| 86 | + """ |
| 87 | + Function updates the largest_square_area[0], if recursive call found |
| 88 | + square with maximum area. |
| 89 | +
|
| 90 | + We are using dp_array here, so the time complexity would be O(N^2). |
| 91 | +
|
| 92 | + >>> largest_square_area_in_matrix_top_down_approch_with_dp(2, 2, [[1,1], [1,1]]) |
| 93 | + 2 |
| 94 | + >>> largest_square_area_in_matrix_top_down_approch_with_dp(2, 2, [[0,0], [0,0]]) |
| 95 | + 0 |
| 96 | + """ |
| 97 | + |
| 98 | + def update_area_of_max_square_using_dp_array( |
| 99 | + row: int, col: int, dp_array: list[list[int]] |
| 100 | + ) -> int: |
| 101 | + if row >= rows or col >= cols: |
| 102 | + return 0 |
| 103 | + if dp_array[row][col] != -1: |
| 104 | + return dp_array[row][col] |
| 105 | + |
| 106 | + right = update_area_of_max_square_using_dp_array(row, col + 1, dp_array) |
| 107 | + diagonal = update_area_of_max_square_using_dp_array(row + 1, col + 1, dp_array) |
| 108 | + down = update_area_of_max_square_using_dp_array(row + 1, col, dp_array) |
| 109 | + |
| 110 | + if mat[row][col]: |
| 111 | + sub_problem_sol = 1 + min([right, diagonal, down]) |
| 112 | + largest_square_area[0] = max(largest_square_area[0], sub_problem_sol) |
| 113 | + dp_array[row][col] = sub_problem_sol |
| 114 | + return sub_problem_sol |
| 115 | + else: |
| 116 | + return 0 |
| 117 | + |
| 118 | + largest_square_area = [0] |
| 119 | + dp_array = [[-1] * cols for _ in range(rows)] |
| 120 | + update_area_of_max_square_using_dp_array(0, 0, dp_array) |
| 121 | + |
| 122 | + return largest_square_area[0] |
| 123 | + |
| 124 | + |
| 125 | +def largest_square_area_in_matrix_bottom_up( |
| 126 | + rows: int, cols: int, mat: list[list[int]] |
| 127 | +) -> int: |
| 128 | + """ |
| 129 | + Function updates the largest_square_area, using bottom up approach. |
| 130 | +
|
| 131 | + >>> largest_square_area_in_matrix_bottom_up(2, 2, [[1,1], [1,1]]) |
| 132 | + 2 |
| 133 | + >>> largest_square_area_in_matrix_bottom_up(2, 2, [[0,0], [0,0]]) |
| 134 | + 0 |
| 135 | +
|
| 136 | + """ |
| 137 | + dp_array = [[0] * (cols + 1) for _ in range(rows + 1)] |
| 138 | + largest_square_area = 0 |
| 139 | + for row in range(rows - 1, -1, -1): |
| 140 | + for col in range(cols - 1, -1, -1): |
| 141 | + |
| 142 | + right = dp_array[row][col + 1] |
| 143 | + diagonal = dp_array[row + 1][col + 1] |
| 144 | + bottom = dp_array[row + 1][col] |
| 145 | + |
| 146 | + if mat[row][col] == 1: |
| 147 | + dp_array[row][col] = 1 + min(right, diagonal, bottom) |
| 148 | + largest_square_area = max(dp_array[row][col], largest_square_area) |
| 149 | + else: |
| 150 | + dp_array[row][col] = 0 |
| 151 | + |
| 152 | + return largest_square_area |
| 153 | + |
| 154 | + |
| 155 | +def largest_square_area_in_matrix_bottom_up_space_optimization( |
| 156 | + rows: int, cols: int, mat: list[list[int]] |
| 157 | +) -> int: |
| 158 | + """ |
| 159 | + Function updates the largest_square_area, using bottom up |
| 160 | + approach. with space optimization. |
| 161 | +
|
| 162 | + >>> largest_square_area_in_matrix_bottom_up_space_optimization(2, 2, [[1,1], [1,1]]) |
| 163 | + 2 |
| 164 | + >>> largest_square_area_in_matrix_bottom_up_space_optimization(2, 2, [[0,0], [0,0]]) |
| 165 | + 0 |
| 166 | + """ |
| 167 | + current_row = [0] * (cols + 1) |
| 168 | + next_row = [0] * (cols + 1) |
| 169 | + largest_square_area = 0 |
| 170 | + for row in range(rows - 1, -1, -1): |
| 171 | + for col in range(cols - 1, -1, -1): |
| 172 | + |
| 173 | + right = current_row[col + 1] |
| 174 | + diagonal = next_row[col + 1] |
| 175 | + bottom = next_row[col] |
| 176 | + |
| 177 | + if mat[row][col] == 1: |
| 178 | + current_row[col] = 1 + min(right, diagonal, bottom) |
| 179 | + largest_square_area = max(current_row[col], largest_square_area) |
| 180 | + else: |
| 181 | + current_row[col] = 0 |
| 182 | + next_row = current_row |
| 183 | + |
| 184 | + return largest_square_area |
| 185 | + |
| 186 | + |
| 187 | +if __name__ == "__main__": |
| 188 | + import doctest |
| 189 | + |
| 190 | + doctest.testmod() |
| 191 | + print(largest_square_area_in_matrix_bottom_up(2, 2, [[1, 1], [1, 1]])) |
0 commit comments