AlgoMaster Logo

Count Square Submatrices with All Ones

mediumFrequency8 min readUpdated June 23, 2026

Understanding the Problem

We are given a 2D binary matrix of integers (0s and 1s) and we need to count every square submatrix that consists entirely of 1s. A 1x1 cell with value 1 counts as a square. A 2x2 block of all 1s counts as one more square. A 3x3 block of all 1s adds yet another, and so on.

We are counting squares of every size, not finding the largest one. If a cell can be the bottom-right corner of a 3x3 all-ones square, it is also the bottom-right corner of a 2x2 and a 1x1 all-ones square. So a cell whose largest square has side length k contributes exactly k squares to the total count. The DP approach builds on this observation.

Key Constraints:

  • 1 <= m, n <= 300 → The matrix has at most 90,000 cells, so an O(m * n) pass is fast. Verifying every candidate square cell by cell is far more expensive (on the order of 10^11 cell reads for a 300x300 all-ones matrix), which rules out the brute force and points toward computing each cell's answer from its neighbors.
  • 0 <= arr[i][j] <= 1 → The values are integers 0 and 1. The answer itself stays small: even for a 300x300 all-ones matrix the total count is about 9 million, so a 32-bit integer is enough.

Approach 1: Brute Force

Intuition

For every possible top-left corner (i, j) and every possible side length k, check whether all k*k cells inside that square are 1. If they are, count it. This implements the definition directly.

For each cell where matrix[i][j] == 1, we try side lengths 1, 2, 3, ... as long as the square fits within the matrix, scanning every cell inside each candidate square. Once a side length fails, we can stop expanding from that corner: any larger square from the same corner contains the failing square, so it contains the same 0.

Algorithm

  1. Initialize count = 0.
  2. For each cell (i, j) in the matrix:
    • Determine the maximum possible side length: maxK = min(m - i, n - j).
    • For each side length k from 1 to maxK:
      • Check if all cells in the square from (i, j) to (i+k-1, j+k-1) are 1.
      • If yes, increment count.
      • If no, stop expanding from this corner.
  3. Return count.

Example Walkthrough

Input:

0
1
2
3
0
0
1
1
1
1
1
1
1
1
2
0
1
1
1
matrix

Each cell (i, j) is the top-left corner of the candidate squares, which grow down and to the right.

  • Row 0: (0,0) is 0, skip. (0,1): the 1x1, 2x2, and 3x3 squares all pass (the 3x3 covers rows 0-2, columns 1-3, all ones), and a 4x4 does not fit, so this corner contributes 3. (0,2): 1x1 and 2x2 pass; a 3x3 does not fit because only 2 columns remain. Contributes 2. (0,3): only a 1x1 fits. Contributes 1.
  • Row 1: (1,0): 1x1 passes, but the 2x2 contains (2,0) = 0, so we stop. Contributes 1. (1,1): 1x1 and 2x2 pass (rows 1-2, columns 1-2 are all ones); a 3x3 does not fit. Contributes 2. (1,2): same shape, contributes 2. (1,3): only a 1x1 fits. Contributes 1.
  • Row 2: (2,0) is 0, skip. (2,1), (2,2), (2,3): only 1x1 squares fit. Each contributes 1.

Total = 3 + 2 + 1 + 1 + 2 + 2 + 1 + 1 + 1 + 1 = 15, which is the returned answer.

15
count

Code

Every square is verified from scratch even though it overlaps heavily with squares already checked. Dynamic programming removes the repeated scanning: the largest all-ones square ending at a cell can be computed in O(1) from three already-computed neighbors.

Approach 2: 2D Dynamic Programming

Intuition

Define dp[i][j] as the side length of the largest all-ones square whose bottom-right corner is at position (i, j). If matrix[i][j] is 0, then dp[i][j] = 0 since no all-ones square can end at a 0 cell.

For a cell where matrix[i][j] == 1, being the bottom-right corner of a k x k square requires the cell above, the cell to the left, and the cell diagonally above-left to each be the bottom-right corner of an all-ones square of side at least k-1. The weakest neighbor limits how far the square extends, giving the recurrence:

dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1

The same table answers the counting question. If dp[i][j] = 3, that cell is the bottom-right corner of a 3x3, a 2x2, and a 1x1 all-ones square, so it contributes exactly 3 squares. Summing all dp values counts every square exactly once, because each square has exactly one bottom-right corner.

Algorithm

  1. Create a 2D array dp of size m x n, initialized to 0.
  2. Initialize count = 0.
  3. For each cell (i, j) where matrix[i][j] == 1:
    • If i == 0 or j == 0 (first row or first column), set dp[i][j] = 1.
    • Otherwise, dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1.
    • Add dp[i][j] to count.
  4. Return count.

Example Walkthrough

The trace below fills the dp table for matrix = [[0,1,1,1],[1,1,1,1],[0,1,1,1]] (Example 1), row by row.

1Row 0: Copy matrix values. dp[0][0]=0, dp[0][1]=1, dp[0][2]=1, dp[0][3]=1. count=3
0
1
2
3
0
0
1
1
1
1
0
0
0
0
2
0
0
0
0
1/7

Code

The time is already optimal, but the space matches the input size. Since the recurrence only depends on the current and previous row, we can compress the dp table to a single row.

Approach 3: Space-Optimized DP (1D Array)

Intuition

Computing dp[i][j] reads three values: dp[i-1][j] (same column, previous row), dp[i][j-1] (previous column, current row), and dp[i-1][j-1] (diagonal, previous row). Only the current row and the previous row are needed at any time, and both fit in a single 1D array processed row by row.

The complication is the diagonal value. As we update dp[j] left to right, dp[j] still holds the value from the previous row (that is dp[i-1][j]), and dp[j-1] was already updated for the current row (that is dp[i][j-1]). But we also need dp[i-1][j-1], the old value of dp[j-1] before we overwrote it. So before each update we save the cell's outgoing value in a variable prev, where the next column can read it as the diagonal.

Algorithm

  1. Create a 1D array dp of size n, initialized to 0.
  2. Initialize count = 0 and prev = 0.
  3. For each row i from 0 to m-1:
    • Reset prev = 0 at the start of each row.
    • For each column j from 0 to n-1:
      • Save temp = dp[j] (this will become prev for the next column).
      • If matrix[i][j] == 1: if i == 0 or j == 0, set dp[j] = 1; otherwise dp[j] = min(dp[j], dp[j-1], prev) + 1. Add dp[j] to count.
      • Otherwise, set dp[j] = 0.
      • Set prev = temp.
  4. Return count.

Example Walkthrough

This trace uses matrix = [[1,0,1],[1,1,0],[1,1,0]] (Example 2, answer 7). Only the 1D dp array is shown; it is overwritten in place as each row is processed.

1Initialize dp = [0, 0, 0], count=0, prev=0
0
0
1
0
2
0
1/7

Code