We have a 2D grid of characters where each cell is either '0' or '1'. We need to find the largest axis-aligned square region where every cell is '1', then return the area of that square.
The answer is an area, not a side length, so a 3x3 square returns 9. We want squares specifically, not rectangles. The matrix can be up to 300x300, which is 90,000 cells, so the approach has to scale.
The efficient solutions all rest on one reframing: for each cell, what is the largest all-1's square that can end at that cell, using it as the bottom-right corner? If we can answer that for every cell, the largest answer is the side length we want.
1 <= m, n <= 300 → The matrix has at most 90,000 cells. An O(m n) solution is about 90,000 operations. A naive O(m^2 n^2) check of every possible square is around 8 billion operations, which is too slow, so the goal is to reach O(m * n).matrix[i][j] is '0' or '1' → The cells are characters, not integers. Comparisons must be against '1' and '0', not the numbers 1 and 0.Try every possible square in the matrix. For each cell, treat it as the top-left corner of a square and expand the side length as far as possible while all cells within the square remain '1'. Track the largest square found.
For a given top-left corner (i, j) and side length k, verify that all k*k cells inside the square are '1'. If any cell is '0', that side length (and anything bigger) cannot work for this corner, so the expansion stops there.
maxSide = 0 to track the largest square side length found.matrix[i][j] == '1':maxK = min(m - i, n - j).maxSide = max(maxSide, k).maxSide * maxSide.Input:
We try each '1' cell as a top-left corner and expand. Most corners reach only side 1 before hitting a '0'. For example, corner (0,0) cannot reach side 2 because the 2x2 square would include (0,1), which is '0'. Corner (2,1) also stops at side 1 because the 2x2 square would include (3,2), which is '0'.
The best corner is (1,2). A 2x2 square there covers rows 1-2 and columns 2-3, all of which are '1', so side 2 works. Expanding to 3x3 would need rows 1-3 and columns 2-4, but (3,2) is '0', so it fails. No other corner produces a square larger than side 2. The maximum side is 2, so the area is 2 * 2 = 4.
This approach re-scans overlapping regions for every cell. The next approach removes that repeated work by computing the largest square ending at each cell from results already computed for its neighbors.
Instead of asking "what is the biggest square starting at this cell?", ask "what is the biggest square whose bottom-right corner is this cell?" That second question has a recurrence the first does not.
Define dp[i][j] as the side length of the largest all-1's square with its bottom-right corner at position (i, j). If matrix[i][j] is '0', then dp[i][j] = 0 since no all-1's square can end at a '0' cell.
For cell (i, j) to be the bottom-right corner of a k x k square of 1's, three conditions must hold at once:
If any of these three neighbors has a smaller value, that neighbor limits how far the square can extend. The recurrence is:
dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
A square of side k with bottom-right corner at (i, j) can be decomposed into three overlapping squares of side k-1: one ending at (i-1, j) covering the top, one ending at (i, j-1) covering the left, and one ending at (i-1, j-1) covering the top-left interior. Together with cell (i, j) itself being '1', these three regions cover the whole k x k area. So a square of side k exists at (i, j) only if all three neighbors support a square of side at least k-1.
That is why the recurrence takes the minimum of the three neighbors. If even one neighbor supports only a smaller square, the gap it leaves means no k x k square can fit, no matter how large the other two are.
dp of size (m+1) x (n+1), initialized to 0. The extra row and column handle boundary conditions cleanly.maxSide = 0.matrix[i-1][j-1] == '1' (using 1-indexed dp with 0-indexed matrix):dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1maxSide = max(maxSide, dp[i][j])maxSide * maxSide.Using the same matrix as before, we fill a dp grid one row at a time. Each '1' cell takes the minimum of its top, left, and diagonal neighbors and adds 1; each '0' cell stays 0. The animation below tracks the dp values and the running maximum.
The largest dp value is 2, reached at (2,3) and (2,4). That marks a 2x2 square of 1's ending at those corners, so the area is 2 * 2 = 4.
The dp table uses O(m * n) space, but computing row i only reads row i and row i-1. The next approach keeps a single row, dropping the space to O(n).
In the recurrence dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1, all three values come from either the current row or the immediately preceding row. The 2D table can collapse into a single 1D array of length n+1. As we process each row left to right, the array transitions from holding the previous row's values to holding the current row's.
The one complication is dp[i-1][j-1], the diagonal. By the time we process column j, position j-1 in the array already holds the current row's value, so the previous row's diagonal value has been overwritten. We keep it in a separate variable, saved one step before it gets overwritten.
When we reach column j in the current row, the three recurrence values map to:
dp[j], not yet updated this row, still holds the previous row's value at column j. That is dp[i-1][j], the cell above.dp[j-1], already updated this row, holds the current row's value at column j-1. That is dp[i][j-1], the cell to the left.prev holds the value dp[j-1] had before this row updated it, which is the previous row's value at column j-1. That is dp[i-1][j-1], the diagonal.All three are available with one array and one extra variable. Resetting prev = 0 at the start of each row supplies the correct diagonal for column 1, whose top-left neighbor lies outside the grid.
dp of size n+1, initialized to 0.maxSide = 0 and a variable prev = 0 to track the diagonal value.dp[j] in temp (this will be prev for the next column).matrix[i][j-1] == '1':dp[j] = min(dp[j], dp[j-1], prev) + 1maxSide if needed.dp[j] = 0.prev = temp.prev = 0 at the start of each row.maxSide * maxSide.This trace uses a 3x3 matrix of all 1's, which grows the square in every row and exercises the diagonal carry through prev. The dp array has length n+1 = 4. Index 0 stays 0 as a left boundary, and indices 1 through 3 hold the current row's values for columns 0 through 2 of the matrix.
The running maximum reaches 3 when dp[3] becomes 3, which corresponds to the full 3x3 square. The area is 3 * 3 = 9.