给定一个方阵,其中每个单元(像素)非黑即白。设计一个算法,找出 4 条边皆为黑色像素的最大子方阵。
返回一个数组 [r, c, size] ,其中 r, c 分别代表子方阵左上角的行号和列号,size 是子方阵的边长。若有多个满足条件的子方阵,返回 r 最小的,若 r 相同,返回 c 最小的子方阵。若无满足条件的子方阵,返回空数组。
leetcode
解题思路:这道题和之前遇到的寻找最大正方形很类似,不同的是,这里只需要一个边框就行了,所以更加麻烦一些。
- 首先通过DP计算以每个元素为起点,向下的和向右的最大边长;
- 然后枚举所有点,计算最大正方形;
- 枚举一个点的时候,再比较枚举另外两条边,这样就能获得一个最大的正方形。
class Solution {
public int[] findSquare(int[][] matrix) {
int n = matrix.length;
if(n == 0) return new int[0];
int m = matrix[0].length;
int[][] row = new int[n + 1][m + 1];
int[][] col = new int[n + 1][m + 1];
int[] res = new int[3];
// 计算每个点向下和向右的最大边长,从最后开始
for(int i = n - 1; i >= 0; i--) {
for(int j = m - 1; j >= 0; j--) {
if(matrix[i][j] == 1) continue;
row[i][j] = row[i][j + 1] + 1;
col[i][j] = col[i + 1][j] + 1;
}
}
// 枚举每个点的最大正方形
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
// 获取以该点为顶底的可能的最大正方形边长
int cur = Math.min(col[i][j], row[i][j]);
// 小于当前答案,那就没必要再枚举了
if(cur < res[2]) continue;
// 再去从大到小枚举边长,是否能够成正方形
for(int x = cur; x > 0; x--) {
// 查看对应的点的边长是否符合要求
if(Math.min(row[i + x - 1][j], col[i][j + x - 1]) >= x) {
if(res[2] < x) {
// 更新答案
res = new int[]{i, j, x};
}
// 已经枚举到最大的边长了,就没必要再枚举小的了
break;
}
}
}
}
return res[2] == 0 ? new int [0] : res;
}
}