[LeetCode] 1277. Count Square Submatrices with All Ones 统计全为 1 的正方形子矩阵
Given a m * n
matrix of ones and zeros, return how many square submatrices have all ones.
Example 1:
Input: matrix =
[
[0,1,1,1],
[1,1,1,1],
[0,1,1,1]
]
Output: 15
Explanation:
There are 10 squares of side 1.
There are 4 squares of side 2.
There is 1 square of side 3.
Total number of squares = 10 + 4 + 1 = 15.
Example 2:
Input: matrix =
[
[1,0,1],
[1,1,0],
[1,1,0]
]
Output: 7
Explanation:
There are 6 squares of side 1.
There is 1 square of side 2.
Total number of squares = 6 + 1 = 7.
Constraints:
1 <= arr.length <= 300
1 <= arr[0].length <= 300
0 <= arr[i][j] <= 1
这道题说是给了一个 m by n 大小的二维数组,只有0和1,现在问有多少个只包含1的正方形子数组。这里需要满足两个条件,一个是正方形的子数组,另一个是都必须包含1。既然都是包含1,而且还是正方形,那么就可以快速的知道这个正方形子数组的数字之和,正好等于该正方形边长的平方,那么一个比较直接的检测方法就是遍历每个正方形子数组,检查其数字之和是否为边长的平方。为了能快速的获得任意一个子数组的数字之和,可以建立累加和数组,在一维子数组问题中这个很常见,二维数组中其实也差不了太多,只不过更新的时候稍微麻烦一点。这里的二维累加和数组行和列数各多一个,可以防止越界,更新的方法就是原数组的数字加上累加和数组对应位置的左边,上边,和左上边的数字之和。更新好了累加和数组之和,就可以来找正方形子数组了,遍历原数组中的所有位置,当作正方形的左上顶点,然后在可允许的范围内,遍历正方形的长度,然后根据累加和数组快速计算出子数组数字之和,跟正方形边长的平方比较,若相等,则结果 res 自增1即可,参见代码如下:
解法一:
class Solution {
public:
int countSquares(vector<vector<int>>& matrix) {
int m = matrix.size(), n = matrix[0].size(), res = 0;
vector<vector<int>> sums(m + 1, vector<int>(n + 1));
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
sums[i][j] = matrix[i - 1][j - 1] + sums[i - 1][j] + sums[i][j - 1] - sums[i - 1][j - 1];
}
}
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
for (int k = 0; i + k <= m && j + k <= n; ++k) {
if (sums[i + k][j + k] - sums[i - 1][j + k] - sums[i + k][j - 1] + sums[i - 1][j - 1] == (k + 1) * (k + 1)) {
++res;
}
}
}
}
return res;
}
};
上面的方法虽然可以通过建立累加和数组来提高效率,但仍是立方级的时间复杂度。这里可以使用动态规划 Dynamic Programming 来降到平方级的复杂度,这里定义一个二维 dp 数组,其中 dp[i][j] 表示以 (i, j) 为右下顶点的最大的正方形子数组的边长,同时正好也是以 (i, j) 为右下顶点的正方形子数组的个数,这个不理解的童鞋可以举些例子观察一下。那么只要求出每个位置的 dp 值,然后都加起来,就是题目要求的所有的正方形子数组的个数。dp 数组可以直接就初始化为 matrix,因为每个为1的点,正好也是一个边长为1的正方形子数组,满足 dp 的定义。然后就要进行状态转移了,更新的位置必须要是 dp 值大于0的地方,并且不能是第一行或者第一列,不然正方形边长不可能超过1。更新的状态转移方程就是看当前位置的左边,上边,和左上边三个位置中的最小值,并加上1。若那三个位置的值都不为0的话,就保证了必然有大于1的正方形存在,就算最小值为0,加上1后,至少 dp 的值不会改变(因为初始化时赋值为1了),每次更新完 dp 值后,将其加到结果 res 中即可,参见代码如下:
解法二:
class Solution {
public:
int countSquares(vector<vector<int>>& matrix) {
int m = matrix.size(), n = matrix[0].size(), res = 0;
vector<vector<int>> dp = matrix;
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (dp[i][j] > 0 && i > 0 && j > 0) {
dp[i][j] = min({dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]}) + 1;
}
res += dp[i][j];
}
}
return res;
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/1277
类似题目:
Minimum Cost Homecoming of a Robot in a Grid
Count Fertile Pyramids in a Land
参考资料:
https://leetcode.com/problems/count-square-submatrices-with-all-ones/