221. 最大正方形
思路:在一个由 ‘0’ 和 ‘1’ 组成的二维矩阵内,找到只包含 ‘1’ 的最大正方形,并返回其面积。
输入:
matrix = [["1","0","1","0","0"],
["1","0","1","1","1"],
["1","1","1","1","1"],
["1","0","0","1","0"]]
输出:4
每当看到这种正方形或者矩形的题目,都会形成一种本能的反应,就是找到两个点,即左上和右下顶点,因为固定这两个点,整个正方形也就固定了。
首先采用暴力法,也就是对每一个点,遍历其可能出现的所有正方形的长度,返回满足条件的最长的就可以了:
public int maximalSquare(char[][] matrix) {
int max = 0;
for (int i = 0; i < matrix.length; i++)
for (int j = 0; j < matrix[0].length; j++)
if (matrix[i][j] == '1') max = Math.max(max, function(matrix, i, j));
return max;
}
public int function(char[][] matrix, int i, int j) {
int m = 0;
for (; m + j < matrix[0].length && m + i < matrix.length; m++) {
for (int p = j; p <= j + m; p++)
if (matrix[m+i][p] != '1') return m * m;
for (int q = i; q < i + m; q++)
if (matrix[q][m+j] != '1') return m * m;
}
return m * m;
}
另一种方式就是动态规划,这需要我们发现一个规律来更新动态方程,我们定义dp[i][j]表示以第i行第j列为右下角所能构成的最大正方形边长,那么如何更新呢?
当我们判断以某个点为正方形右下角时最大的正方形时,那它的上方,左方和左上方三个点也一定是某个正方形的右下角,否则该点为右下角的正方形最大就是它自己了。这是定性的判断,那具体的最大正方形边长呢?我们知道,该点为右下角的正方形的最大边长,最多比它的上方,左方和左上方为右下角的正方形的边长多1,最好的情况是是它的上方,左方和左上方为右下角的正方形的大小都一样的,这样加上该点就可以构成一个更大的正方形。 但如果它的上方,左方和左上方为右下角的正方形的大小不一样,合起来就会缺了某个角落,这时候只能取那三个正方形中最小的正方形的边长加1了。假设dpi表示以i,j为右下角的正方形的最大边长,则有 :
dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1
举个例子,如果dp[i-1][j-1], dp[i-1][j], dp[i][j-1]位置其中有一个位置为0,都是证明无法构成正方形的,因此dp[i][j] = 0+1 = 1,也就是只有自身的一个正方形,同样的道理,再举个例,如果dp[i-1][j-1], dp[i-1][j], dp[i][j-1]分别为123,那么无论如何都是可以构成2乘2的正方形的,画个图就可以清晰明了啦~~
代码很简单,也不需要初始化数组,因为其他位置默认为0就可以啦:
public int maximalSquare(char[][] matrix) {
/**
dp[i][j]表示以第i行第j列为右下角所能构成的最大正方形边长, 则递推式为:
dp[i][j] = 1 + min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]);
**/
int m = matrix.length;
if(m < 1) return 0;
int n = matrix[0].length;
int max = 0;
int[][] dp = new int[m+1][n+1];
for(int i = 1; i <= m; ++i) {
for(int j = 1; j <= n; ++j) {
if(matrix[i-1][j-1] == '1') {
dp[i][j] = 1 + Math.min(dp[i-1][j-1], Math.min(dp[i-1][j], dp[i][j-1]));
max = Math.max(max, dp[i][j]);
}
}
}
return max*max;
}