Given a 2D binary matrix filled with 0's and 1's, find the largest square containing all 1's and return its area.

For example, given the following matrix:

1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0

Return 4.

Credits:
Special thanks to @Freezen for adding this problem and creating all test cases.

这道题我刚看到的时候,马上联想到了之前的一道 Number of Islands,但是仔细一对比,发现又不太一样,那道题1的形状不确定,很适合 DFS 的特点,而这道题要找的是正方形,是非常有特点的形状,所以并不需要用到 DFS,要论相似,我倒认为这道 Maximal Rectangle 更相似一些。这道题的解法不止一种,我们先来看一种 brute force 的方法,这种方法的机理就是就是把数组中每一个点都当成正方形的左顶点来向右下方扫描,来寻找最大正方形。具体的扫描方法是,确定了左顶点后,再往下扫的时候,正方形的竖边长度就确定了,只需要找到横边即可,这时候我们使用直方图的原理,从其累加值能反映出上面的值是否全为1,之前也有一道关于直方图的题 Largest Rectangle in Histogram。通过这种方法我们就可以找出最大的正方形,参见代码如下:

解法一:

class Solution {
public:
int maximalSquare(vector<vector<char> >& matrix) {
int res = ;
for (int i = ; i < matrix.size(); ++i) {
vector<int> v(matrix[i].size(), );
for (int j = i; j < matrix.size(); ++j) {
for (int k = ; k < matrix[j].size(); ++k) {
if (matrix[j][k] == '') ++v[k];
}
res = max(res, getSquareArea(v, j - i + ));
}
}
return res;
}
int getSquareArea(vector<int> &v, int k) {
if (v.size() < k) return ;
int count = ;
for (int i = ; i < v.size(); ++i) {
if (v[i] != k) count = ;
else ++count;
if (count == k) return k * k;
}
return ;
}
};

下面这个方法用到了建立累计和数组的方法,可以参见之前那篇博客 Range Sum Query 2D - Immutable。原理是建立好了累加和数组后,我们开始遍历二维数组的每一个位置,对于任意一个位置 (i, j),我们从该位置往 (0,0) 点遍历所有的正方形,正方形的个数为 min(i,j)+1,由于我们有了累加和矩阵,能快速的求出任意一个区域之和,所以我们能快速得到所有子正方形之和,比较正方形之和跟边长的平方是否相等,相等说明正方形中的数字均为1,更新 res 结果即可,参见代码如下:

解法二:

class Solution {
public:
int maximalSquare(vector<vector<char>>& matrix) {
if (matrix.empty() || matrix[].empty()) return ;
int m = matrix.size(), n = matrix[].size(), res = ;
vector<vector<int>> sum(m, vector<int>(n, ));
for (int i = ; i < matrix.size(); ++i) {
for (int j = ; j < matrix[i].size(); ++j) {
int t = matrix[i][j] - '';
if (i > ) t += sum[i - ][j];
if (j > ) t += sum[i][j - ];
if (i > && j > ) t -= sum[i - ][j - ];
sum[i][j] = t;
int cnt = ;
for (int k = min(i, j); k >= ; --k) {
int d = sum[i][j];
if (i - cnt >= ) d -= sum[i - cnt][j];
if (j - cnt >= ) d -= sum[i][j - cnt];
if (i - cnt >= && j - cnt >= ) d += sum[i - cnt][j - cnt];
if (d == cnt * cnt) res = max(res, d);
++cnt;
}
}
}
return res;
}
};

我们还可以进一步的优化时间复杂度到 O(n2),做法是使用 DP,建立一个二维 dp 数组,其中 dp[i][j] 表示到达 (i, j) 位置所能组成的最大正方形的边长。我们首先来考虑边界情况,也就是当i或j为0的情况,那么在首行或者首列中,必定有一个方向长度为1,那么就无法组成长度超过1的正方形,最多能组成长度为1的正方形,条件是当前位置为1。边界条件处理完了,再来看一般情况的递推公式怎么办,对于任意一点 dp[i][j],由于该点是正方形的右下角,所以该点的右边,下边,右下边都不用考虑,关心的就是左边,上边,和左上边。这三个位置的dp值 suppose 都应该算好的,还有就是要知道一点,只有当前 (i, j) 位置为1,dp[i][j] 才有可能大于0,否则 dp[i][j] 一定为0。当 (i, j) 位置为1,此时要看 dp[i-1][j-1], dp[i][j-1],和 dp[i-1][j] 这三个位置,我们找其中最小的值,并加上1,就是 dp[i][j] 的当前值了,这个并不难想,毕竟不能有0存在,所以只能取交集,最后再用 dp[i][j] 的值来更新结果 res 的值即可,参见代码如下:

解法三:

class Solution {
public:
int maximalSquare(vector<vector<char>>& matrix) {
if (matrix.empty() || matrix[].empty()) return ;
int m = matrix.size(), n = matrix[].size(), res = ;
vector<vector<int>> dp(m, vector<int>(n, ));
for (int i = ; i < m; ++i) {
for (int j = ; j < n; ++j) {
if (i == || j == ) dp[i][j] = matrix[i][j] - '';
else if (matrix[i][j] == '') {
dp[i][j] = min(dp[i - ][j - ], min(dp[i][j - ], dp[i - ][j])) + ;
}
res = max(res, dp[i][j]);
}
}
return res * res;
}
};

下面这种解法进一步的优化了空间复杂度,此时只需要用一个一维数组就可以解决,为了处理边界情况,padding 了一位,所以 dp 的长度是 m+1,然后还需要一个变量 pre 来记录上一个层的 dp 值,我们更新的顺序是行优先,就是先往下遍历,用一个临时变量t保存当前 dp 值,然后看如果当前位置为1,则更新 dp[i] 为 dp[i], dp[i-1], 和 pre 三者之间的最小值,再加上1,来更新结果 res,如果当前位置为0,则重置当前 dp 值为0,因为只有一维数组,每个位置会被重复使用,参见代码如下:

解法四:

class Solution {
public:
int maximalSquare(vector<vector<char>>& matrix) {
if (matrix.empty() || matrix[].empty()) return ;
int m = matrix.size(), n = matrix[].size(), res = , pre = ;
vector<int> dp(m + , );
for (int j = ; j < n; ++j) {
for (int i = ; i <= m; ++i) {
int t = dp[i];
if (matrix[i - ][j] == '') {
dp[i] = min(dp[i], min(dp[i - ], pre)) + ;
res = max(res, dp[i]);
} else {
dp[i] = ;
}
pre = t;
}
}
return res * res;
}
};

类似题目:

Maximal Rectangle

Largest Rectangle in Histogram

参考资料:

https://leetcode.com/problems/maximal-square/

https://leetcode.com/problems/maximal-square/discuss/61803/c-dynamic-programming

https://leetcode.com/problems/maximal-square/discuss/61913/my-concise-solution-in-c

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] 221. Maximal Square 最大正方形的更多相关文章

  1. 求解最大正方形面积 — leetcode 221. Maximal Square

    本来也想像园友一样,写一篇总结告别 2015,或者说告别即将过去的羊年,但是过去一年发生的事情,实在是出乎平常人的想象,也不具有代表性,于是计划在今年 6 月份写一篇 "半年总结" ...

  2. (medium)LeetCode 221.Maximal Square

    Given a 2D binary matrix filled with 0's and 1's, find the largest square containing all 1's and ret ...

  3. 221 Maximal Square 最大正方形

    在一个由0和1组成的二维矩阵内,寻找只包含1的最大正方形,并返回其面积.例如,给出如下矩阵:1 0 1 0 01 0 1 1 11 1 1 1 11 0 0 1 0返回 4. 详见:https://l ...

  4. Leetcode 221. Maximal Square

    本题用brute force超时.可以用DP,也可以不用. dp[i][j] 代表 以(i,j)为右下角正方形的边长. class Solution(object): def maximalSquar ...

  5. Java for LeetCode 221 Maximal Square

    Given a 2D binary matrix filled with 0's and 1's, find the largest square containing all 1's and ret ...

  6. [LeetCode] 221. Maximal Square _ Medium Tag: Dynamic Programming

    Given a 2D binary matrix filled with 0's and 1's, find the largest square containing only 1's and re ...

  7. leetcode每日解题思路 221 Maximal Square

    问题描述: 题目链接:221 Maximal Square 问题找解决的是给出一个M*N的矩阵, 只有'1', '0',两种元素: 需要你从中找出 由'1'组成的最大正方形.恩, 就是这样. 我们看到 ...

  8. 【刷题-LeetCode】221. Maximal Square

    Maximal Square Given a 2D binary matrix filled with 0's and 1's, find the largest square containing ...

  9. 【LeetCode】221. Maximal Square

    Maximal Square Given a 2D binary matrix filled with 0's and 1's, find the largest square containing ...

随机推荐

  1. Linux高性能服务器编程,书中的 shell 命令

    记录<Linux高性能服务器编程>书里面讲解到的若干 shell 命令 arp 命令查看ARP高速缓存: [root@VM_0_10_centos heliang]# arp -a ? ( ...

  2. GV900 Political Explanation

    GV900 Political Explanation, 2017/201830 October, 2018Homework assignment 2Due Week 7 (13 November)W ...

  3. springboot单元测试@test的使用

    @RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class Springtest { ...

  4. Entity Framework 6 中如何获取 EntityTypeConfiguration 的 Edm 信息?(二)

    接着上一篇 直接贴代码了: using System; using System.Collections.Generic; using System.Data.Entity; using System ...

  5. WPF 隐藏式控件

    没用Popup用的面板控件,全部代码使用xaml的触发器. 代码: <Grid> <DockPanel> <StackPanel Background=" Do ...

  6. Kubernetes Job与CronJob(离线业务)

    Kubernetes Job与CronJob(离线业务) Job Job分为普通任务(Job)  一次性执行 应用场景:离线数据处理,视频解码等业务 官方文档:https://kubernetes.i ...

  7. [Docker] Win10中安装Docker并运行Nginx镜像

    一.安装Docker 进入官网:https://www.docker.com/products/docker-desktop 可能需要先注册登录,很简单的. 点击 Download Desktop f ...

  8. Zabbix触发器和监控项设置时间范围

    目录 一.实际业务场景 业务问题 解决办法 二.Zabbix触发器和监控项与用户预警设置时间范围配置流程 一.触发器设置时间范围 二.监控项设置时间范围 三.用户报警设置启用时间 一.实际业务场景 业 ...

  9. 如何通过 Freemark 优雅地生成那些花里胡哨的复杂样式 Excel 文件?

    欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 高级架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.ex ...

  10. C#刷遍Leetcode系列连载 索引

    C#刷遍Leetcode系列文章 索引 索引(陆续发布中,请保持关注) C#刷遍Leetcode面试题系列连载(1) - 入门与工具简介 C#刷遍Leetcode面试题系列连载(2): No.38 - ...