Given a 2D binary matrix filled with 0's and 1's, find the largest square containing only 1's and return its area.
Example:
Input:
1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
Output: 4
思路:
题目的重点就是,必须是一个正方形内都是1,而且题目给的不是 int 的 vector,是 char 的 vector 需要注意。
看到这种题第一个想法就是先用暴力方式做一次,练练手,然后再用别的方法,我的暴力算方法AC代码如下:
int maximalSquare(vector<vector<char>>& matrix)
{
int m = matrix.size();
if (m == 0)
return 0;
int n = matrix[0].size(); // m行n列
int max_square = 0;
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
if (matrix[i][j] == '1')
{
int tmp_len = 1; // square 限制了只能是正方形,所以只需要记录一个边长
bool flag = true;
while (i + tmp_len < m && j + tmp_len < n && flag)
{
for (int t = 0; t <= tmp_len; t++)
{
if (matrix[i + tmp_len][j + t] == '0'
|| matrix[i + t][j + tmp_len] == '0')
{
flag = false;
break;
}
}
if (flag == true)
++tmp_len;
}
max_square = max(max_square, tmp_len * tmp_len);
}
}
}
return max_square; // 当没有1的时候,返回0
}
没有想到的是,这种方法12ms,击败了 98.72 的AC代码!!!
然后基于上边的代码,我又想了想加速的方式,一个是,没必要每次计算面积,只需要每次记录边长,最后做一次乘法返回即可,这样能少做很多次乘法;第二点是,当比如说我目前的最大边长是100的情况下, 倒数99行以及倒数99列之后的都不需要遍历了,因为不可能比目前的大了,这样按理说能加速不少。AC代码如下:
int maximalSquare(vector<vector<char>>& matrix)
{
int m = matrix.size();
if (m == 0)
return 0;
int n = matrix[0].size(); // m行n列
int max_len = 0; // 当没有1的时候,返回0
for (int i = 0; i < m - max_len; i++) // 根据目前最大边长,减少遍历范围!
{
for (int j = 0; j < n - max_len; j++)
{
if (matrix[i][j] == '1')
{
int tmp_len = 1; // square 限制了只能是正方形,所以只需要记录一个边长
bool flag = true;
while (i + tmp_len < m && j + tmp_len < n && flag)
{
for (int t = 0; t <= tmp_len; t++)
{
if (matrix[i + tmp_len][j + t] == '0' || matrix[i + t][j + tmp_len] == '0')
{
flag = false;
break;
}
}
if (flag == true)
++tmp_len;
}
max_len = max(max_len, tmp_len); // 每次不算面积,最后再算,能加速一些
}
}
}
return max_len * max_len;
}
然而神奇的是,这样的结果是28ms,只击败7%的提交。很是不解= =,望大神指点(我觉得是测试样例的问题吧,暴力破解能击败98%的人也是不敢相信)。
暴力方法过了,就要想想其他的方式了,这种 m*n 的矩阵内求最值得问题,应该都能用DP来做。
首先是创建一个和原始矩阵一样大小的矩阵 dp(其实行列都是要大1的,为了方便),里边的元素值全设为0,然后从左上遍历到右下,原始矩阵中的0不处理,对应dp矩阵中也就是0不变,对原始矩阵中的1,dp矩阵中对应值按一下方式计算:
dp(i, j) = min(dp(i−1, j), dp(i−1, j−1), dp(i, j−1)) + 1
这样就能够得到以每个位置为右下角的最大的全1矩阵边长,如下图所示:
dp矩阵中的“3”就表示,以这个位置为右下角,可以有边长为3的全1矩阵,在原始就很中可以很清楚的看到。这种方式就不能用上边的减少遍历范围的方式加速,因为不到最后边不知道会不会有更大的。
AC代码如下:
int maximalSquare(vector<vector<char>>& matrix)
{
int m = matrix.size();
if(m == 0)
return 0;
int n = matrix[0].size(); // m行n列
vector<vector<int>> dp = vector<vector<int>>(m + 1, vector<int>(n + 1, 0));
int max_len = 0;
for (int i = 1; i <= m; i++)
{
for (int j = 1; j <= n; j++)
{
if (matrix[i-1][j-1] == '1')
{
dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1;
max_len = max(max_len, dp[i][j]);
}
}
}
return max_len * max_len;
}
这个代码也是98%的击败,按理说肯定是比暴力要快很多的。
网上还有用更小的空间复杂度的代码,但是我感觉空间相对于时间来说并不重要,所以,就这样吧 = =。