class Solution:
def countSquares(self, matrix):
if not matrix or not matrix[0]:
return 0
m, n = len(matrix), len(matrix[0])
dp = [[0]*n for _ in range(m)]
total = 0
for i in range(m):
for j in range(n):
if matrix[i][j] == 1:
if i == 0 or j == 0:
dp[i][j] = 1
else:
dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
total += dp[i][j]
return total
class Solution {
public:
int countSquares(vector<vector<int>>& matrix) {
int m = matrix.size(), n = matrix[0].size(), total = 0;
vector<vector<int>> dp(m, vector<int>(n, 0));
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (matrix[i][j] == 1) {
if (i == 0 || j == 0)
dp[i][j] = 1;
else
dp[i][j] = min({dp[i-1][j], dp[i][j-1], dp[i-1][j-1]}) + 1;
total += dp[i][j];
}
}
}
return total;
}
};
class Solution {
public int countSquares(int[][] matrix) {
int m = matrix.length, n = matrix[0].length, total = 0;
int[][] dp = new int[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (matrix[i][j] == 1) {
if (i == 0 || j == 0)
dp[i][j] = 1;
else
dp[i][j] = Math.min(Math.min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1;
total += dp[i][j];
}
}
}
return total;
}
}
var countSquares = function(matrix) {
let m = matrix.length, n = matrix[0].length, total = 0;
let dp = Array.from({length: m}, () => Array(n).fill(0));
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
if (matrix[i][j] === 1) {
if (i === 0 || j === 0)
dp[i][j] = 1;
else
dp[i][j] = Math.min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1;
total += dp[i][j];
}
}
}
return total;
};
You are given a 2D binary matrix called matrix
with dimensions m x n
. Each cell contains either a 0 or a 1.
The task is to count the total number of square submatrices (of any size) that are filled entirely with 1s.
A square submatrix is a square region within the matrix, aligned with the axes, where every cell is 1. The squares can be of any size (1x1, 2x2, ..., up to min(m, n)xmin(m, n)), and can overlap.
Constraints:
The most straightforward way to solve this problem is to check every possible square submatrix and verify if all its cells are 1. However, this brute-force approach would be very slow for large matrices, since there are many possible squares to check.
To optimize, we should look for ways to avoid redundant checks. Notice that if a cell is the bottom-right corner of a square of size k, it must also be the bottom-right corner of squares of size k-1, k-2, ..., 1. This suggests that we can build up the solution using smaller squares to help find larger ones.
Dynamic programming is a good fit for this type of "build up from smaller subproblems" scenario.
We use a dynamic programming (DP) approach to efficiently count all square submatrices with all ones:
dp
of the same size as the input matrix. Each cell dp[i][j]
represents the size of the largest square that ends at cell (i, j)
(i.e., with (i, j)
as its bottom-right corner), and all its cells are 1.
(i, j)
in the matrix:
matrix[i][j] == 0
, then dp[i][j] = 0
(no square ends here).matrix[i][j] == 1
and i == 0
or j == 0
, then dp[i][j] = 1
(only a 1x1 square fits).dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
. This is because the cell can extend the minimal square among its top, left, and top-left neighbors.dp[i][j]
tells you how many squares (of sizes 1 up to dp[i][j]
) end at that cell. Add up all dp[i][j]
values to get the total number of square submatrices with all ones.
This approach ensures that each cell is processed only once, and we avoid redundant checks for overlapping squares.
Let's walk through the following example:
Input matrix: 1 0 1 1 1 0 1 1 0
1 0 1 1 1 0 1 2 0
Thus, there are 7 square submatrices with all ones in this example.
By leveraging dynamic programming, we efficiently count all square submatrices with all ones by building up the solution from smaller squares. Instead of checking every possible square, we use the insight that the largest square ending at a cell depends on its neighbors. This reduces the time complexity from potentially cubic to quadratic, making the solution practical for large matrices. The elegance comes from recognizing overlapping subproblems and using DP to avoid redundant work.