二维前缀和+二分答案:全为1面积最大正方形Ⅲ

二维前缀和+二分答案:全为1面积最大正方形Ⅲ

二维前缀和

  若要求得一个矩阵内任意一个子矩阵中所有数字之和,我们可以使用循环求和,时间复杂度为O(N2),而使用二维前缀和可将此操作优化到O(1)
在这里插入图片描述
  如图所示,pre[2] [3] 即为黄色区域内元素之和,pre[4] [5]为黄色和棕色区域内元素之和。
  如何使用二维前缀和求得子矩阵元素之和?
在这里插入图片描述
  如图所示,要求左上角为(3,3),右下角为(5,5)表示的矩形内元素和,即要求黄色部分的和,我们可以用(1,1)到(5,5)的大矩形减去绿色线围起来的部分,再减去蓝色线围起来的部分,此时绿色和蓝色线重叠的部分被多减了一次,我们再加上它,得到的便是黄色部分的和。用公式表示便是上图所示,因此我们只要知道了二维前缀和数组,便可用该数组将求子矩阵和操作的时间复杂度降到O(1)
  如何求得二维前缀和数组?
  我们是在用双重循环按行序遍历矩阵元素时求二维前缀和数组的,对于当前遍历到的元素A[i] [j],我们要利用之前已计算出的pre[i] [j - 1]和pre[i - 1][j]对pre[i] [j]进行求解
在这里插入图片描述
  如上图所示,要求pre[5] [5],我们先用pre[5] [4] + pre[4] [5],此时棕色部分多加了一次,我们减去棕色部分,得到了蓝色+棕色+黄色部分,再加上A[5] [5],即红色部分,就求出了pre[5] [5]。用公式表示便是上图所示。

问题:

在这里插入图片描述
在这里插入图片描述

思路:

  对于该题,如何判断矩阵是否全为1?由于矩阵为01矩阵,因此我们只要求得矩阵元素和,判断其是否等于面积即可,若相等则该矩阵全为1。有一个很显然但错误的想法,从小到大枚举正方形边长,然后枚举正方形左上角的点,再遍历正方形求出该正方形的元素和,看是否等于面积。但是复杂度过高,为O(N^5)显然不行。我们可以使用二维前缀和优化求元素和过程,将O(N ^ 5)降到O(N ^ 3),但复杂度仍然过高。注意到一个性质:当我们找到了一个边长为len的正方形是合法的,那么显然所有小于len的边长都存在合法的正方形,答案存在单调性,因此可以使用二分答案来枚举,这样能将复杂度最终优化为O(N2logN)

代码:

class Solution {
    public int maximalSquare(char[][] matrix) {
        int n, m, l, r, i, j, mid;
        n = matrix.length;
        m = matrix[0].length;
        int[][] pre = new int[n + 1][m + 1];
        for (i = 0;i <= n;i++) {
            pre[i][0] = 0;
        }
        for (j = 0;j <= m;j++) {
            pre[0][j] = 0;
        }
        for (i = 1;i <= n;i++) {
            for (j = 1;j <= m;j++) {
                pre[i][j] = pre[i - 1][j] + pre[i][j - 1] - pre[i - 1][j - 1] + (matrix[i - 1][j - 1] - '0');
            }
        }
        l = 1;
        r = Math.min(m, n);
        while (l < r) {
            mid = (l + r + 1) / 2;
            if (check(mid, n, m, pre)) {
                l = mid;
            } else {
                r = mid - 1;
            }
        }
        if (pre[n][m] == 0) {
            return 0;
        } else {
            return (l * l);
        }
    }

    static boolean check(int length, int n, int m, int[][] pre) {
        int i, j;
        for (i = 1;i + length - 1 <= n;i++) {
            for (j = 1;j + length - 1 <= m;j++) {
                if (pre[i + length - 1][j + length - 1] - pre[i + length - 1][j - 1] - pre[i - 1][j + length - 1] + pre[i - 1][j - 1] == length * length) {
                    return true;
                }
            }
        }
        return false;
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

happy19991001

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值