列表

详情


面试题 17.23. 最大黑方阵

给定一个方阵,其中每个单元(像素)非黑即白。设计一个算法,找出 4 条边皆为黑色像素的最大子方阵。

返回一个数组 [r, c, size] ,其中 rc 分别代表子方阵左上角的行号和列号,size 是子方阵的边长。若有多个满足条件的子方阵,返回 r 最小的,若 r 相同,返回 c 最小的子方阵。若无满足条件的子方阵,返回空数组。

示例 1:

输入:
[
   [1,0,1],
   [0,0,1],
   [0,0,1]
]
输出: [1,0,2]
解释: 输入中 0 代表黑色,1 代表白色,标粗的元素即为满足条件的最大子方阵

示例 2:

输入:
[
   [0,1,1],
   [1,0,1],
   [1,1,0]
]
输出: [0,0,1]

提示:

原站题解

去查看

上次编辑到这里,代码来自缓存 点击恢复默认模板
class Solution { public: vector<int> findSquare(vector<vector<int>>& matrix) { } };

typescript 解法, 执行用时: 136 ms, 内存消耗: 52.5 MB, 提交时间: 2023-04-22 11:36:38

function findSquare(matrix: number[][]): number[] {
    //    这道题可以这样用
    // 求出每个点的向右和向下最长距离。
    // 然后确定构成矩阵的size,然后查看[i,j]这个点的[i+size-1,j]的右长度有没有大于size
    // 查看[i,j+size-1]这个点的下长度有没有大于size,大于就更新result
    let result = [0, 0, 0];
    let n = matrix.length;
    let m = matrix[0].length;
    let dp: number[][][] = new Array(n)
        .fill(0)
        .map((val) => new Array(m).fill(0).map((val) => new Array(2).fill(0)));
    // ij的右边0的数量
    // dp[i][j][0]=dp[i][j+1][0]+1
    // ij的下边0的数量
    // dp[i][j][1]=dp[i+1][j][1]+1;
    dp[n - 1][m - 1][0] = matrix[n - 1][m - 1] == 0 ? 1 : 0;
    dp[n - 1][m - 1][1] = matrix[n - 1][m - 1] == 0 ? 1 : 0;
    //  求右边,所以必须先初始化最后一列的右边
    for (let i = 0; i < n; i++) dp[i][m - 1][0] = matrix[i][m - 1] == 0 ? 1 : 0;
    for (let i = 0; i < n; i++) {
        for (let j = m - 2; j >= 0; j--) {
            if (matrix[i][j] == 0) dp[i][j][0] = dp[i][j + 1][0] + 1;
        }
    }
    //  求下边,所以必须先初始化最后一行的下边
    for (let j = 0; j < m; j++) dp[n - 1][j][1] = matrix[n - 1][j] == 0 ? 1 : 0;
    for (let j = 0; j < m; j++) {
        for (let i = n - 2; i >= 0; i--) {
            if (matrix[i][j] == 0) dp[i][j][1] = dp[i + 1][j][1] + 1;
        }
    }
    for (let i = 0; i < n; i++) {
        for (let j = 0; j < m; j++) {
            if (matrix[i][j] == 0) {
                // ij可能出现的最大边长
                let max = Math.min(dp[i][j][0], dp[i][j][1]);
                for (let size = max; size >= 0; size--) {
                    // 如果i,j的最大边长为size,则更新
                    if (result[2] >= size) continue;
                    if (
                        dp[i + size - 1][j][0] >= size &&
                        dp[i][j + size - 1][1] >= size
                    ) {
                        result = [i, j, size];
                        // 1,0    2,
                    }
                }
            }
        }
    }
    return result[2] == 0 ? [] : result;
}

python3 解法, 执行用时: 188 ms, 内存消耗: 16.9 MB, 提交时间: 2023-04-22 11:35:52

'''
解题思路
1 阅读题目,比较容易想到的思路就是重构两个矩阵rMatrix和cMatrix(重构成一个矩阵可能更好理解)
对于rMatrix矩阵,rMatrix[row][col]表示从matrix[row][col]开始向右数有多少个0
对于cMatrix矩阵,cMatrix[row][col]表示从matrix[row][col]开始向下数有多少个0
2 重构矩阵后,剩下的就是遍历matrix矩阵
1)对于任意节点[row][col],其能够组成的最大size是min(rMatrix[row][col], cMatrix[row][col])
2)对于任意节点[row][col]和size,能够组成方形的判断条件就是
cMatrix[row][col + size - 1]和rMatrix[row + size - 1][col]不小于size,即
组成方形的右上节点向下数至少有size个0
组成方形的左下节点向右数至少有size个0
'''
class Solution(object):
    def calRMatrix(self, matrix, rMatrix):
        for row in range(len(matrix) - 1, -1, -1):
            count = 0

            for col in range(len(matrix[0]) - 1, -1, -1):
                if matrix[row][col] == 1:
                    count = 0
                else:
                    count += 1

                rMatrix[row][col] = count

        return
        
    def calCMatrix(self, matrix, cMatrix):
        for col in range(len(matrix[0]) - 1, -1, -1):
            count = 0

            for row in range(len(matrix) - 1, -1, -1):
                if matrix[row][col] == 1:
                    count = 0
                else:
                    count += 1

                cMatrix[row][col] = count

        return

    def checkSquareValid(self, rMatrix, cMatrix, row, col, size):
        if row + size - 1 >= len(rMatrix) or col + size - 1 >= len(rMatrix[0]):
            return False

        rightUp = cMatrix[row][col + size - 1]
        leftDonw = rMatrix[row + size - 1][col]

        if rightUp >= size and leftDonw >= size:
            return True

        return False

    def findSquare(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: List[int]
        """
        rMatrix = [[0] * len(matrix[0]) for _ in range(len(matrix))]
        cMatrix = [[0] * len(matrix[0]) for _ in range(len(matrix))]
        resPos = [-1, -1]
        resSize = 0

        self.calRMatrix(matrix, rMatrix)
        self.calCMatrix(matrix, cMatrix)

        for row in range(len(matrix)):
            for col in range(len(matrix[0])):
                maxSize = min(rMatrix[row][col], cMatrix[row][col])

                while maxSize > 0:
                    if self.checkSquareValid(rMatrix, cMatrix, row, col, maxSize):
                        break

                    maxSize -= 1

                if maxSize > resSize:
                    resSize = maxSize
                    resPos = [row, col]

        if resSize == 0:
            return []
        else:
            return resPos + [resSize]

cpp 解法, 执行用时: 100 ms, 内存消耗: 37.1 MB, 提交时间: 2023-04-22 11:34:51

// 动态规划,cnt[r][c][0/1]表示以坐标r,c为起点向左/右最多的连续黑色块的数量
class Solution {
public:
    vector<int> findSquare(vector<vector<int>>& matrix) {
        vector<int> ans(3, 0);
        int n = matrix.size();
        if(n == 0) return {};
        if(n == 1){
            if(matrix[0][0] == 0)
                return {0, 0, 1};
            else
                return {};
        }
        //cnt[r][c][0/1],0右侧,1下侧
        vector<vector<vector<int>>> cnt(n, vector<vector<int>>(n, vector<int>(2)));
        for(int r = n-1; r >= 0; r--){
            for(int c = n-1; c >= 0; c--){
                if(matrix[r][c] == 1)
                    cnt[r][c][0] = cnt[r][c][1] = 0;
                else{
                    //统计cnt[r][c][0/1]
                    if(r < n-1) cnt[r][c][1] = cnt[r+1][c][1] + 1;
                    else cnt[r][c][1] = 1;

                    if(c < n-1) cnt[r][c][0] = cnt[r][c+1][0] + 1;
                    else cnt[r][c][0] = 1;
				   //更新当前最大子方阵
                    int len = min(cnt[r][c][0], cnt[r][c][1]);//最大的可能的边长
                    while(len >= ans[2]){//要答案r,c最小,所以带等号
                        if(cnt[r+len-1][c][0] >= len && cnt[r][c+len-1][1] >= len){
                            //可以构成长为len的方阵
                            ans = {r, c, len};
                            break;
                        }
                        len--;
                    }
                }
            }
        }
        return ans;
    }
};

上一题