问题描述
- 给定一个N*M的矩阵matrix, 在这个矩阵中, 只有0和1两种值, 返回边框全是1的最大正方
形的边长长度。
例如:
0 1 1 1 1
0 1 0 0 1
0 1 0 0 1
0 1 1 1 1
0 1 0 1 1
其中, 边框全是1的最大正方形的大小为4*4, 所以返回4
问题分析
- 首先明确一个问题,便是如果枚举一个N*M的矩阵中的所有的子矩阵,时间复杂度为O(NM)*O(NM),因为一个矩形可由左上角与右下角决定,枚举左上角为O(NM),枚举左上角为O(NM)
那么如果枚举这个矩阵中所有的正方形呢?一个正方形可由左上角的点及其边长所决定,那么时间复杂度为O(NM)*min(N*M),因为边长最多有min(N,M)种情况。 - 所以对于该题,有两种解法:
- 方法1:枚举所有的正方形,然后检查四个边,所以当M=N时,这是O(N^4)时间复杂度
- 方法2:采用预处理矩阵的方法,同样也是枚举所有的正方形,但是判断该正方形是否符合规则是,是O(1)的时间复杂度,所以当M=N时,这是O(N^3)时间复杂度。
这是以空间换时间的做法。
那么如何预处理矩阵呢?
- 用与原矩阵同样大小的两个矩阵,一个为right,一个为down,right[i][j]表示m[i][j]的右边有多少个连续的1,包括自身;down[i][j]表示m[i][j]的下边有多少个连续的1,包括自身。从右到左,从下到上依次填好两个矩阵。
- 对于以[row,col]为左上角,length作为边长的正方形,要想四个边全为1,只有满足如下条件:row,col代表左上方的位置,要求左上方处下边最少有连续的length个1,右边最少有连续的length个1;[row + length - 1][col]代表左下角,要求该点右边最少有连续的length个1;[row][col + length - 1]代表右上角,要求该点下边最少有连续的length个1;这样便确立了四个边都符合规则。
经验教训:
- 预处理矩阵的技巧(举例:如果要求矩阵中某个矩形的和,那么应该如何预处理矩阵)
- 以当前点作为矩形右下角,原矩阵左上角作为新的左上角,求该矩阵的和,那么sum[][]便构造得到,根据sum矩阵,可以得到任意矩阵的和
- 关于该题的复杂度,以及如何更新两个矩阵
代码实现
package advanced_class_04;
public class MyCode_05_MaxOneBorderSize {
public static void setRightAndDown(int[][] m, int [][] right, int[][] down) {
int rowNum = m.length;
int colNum = m[0].length;
right[rowNum - 1][colNum - 1] = m[rowNum - 1][colNum - 1];
down[rowNum - 1][colNum - 1] = m[rowNum - 1][colNum - 1];
for (int col = colNum - 2; col >= 0; col--) {
if (m[rowNum - 1][col] == 1) {
right[rowNum - 1][col] = right[rowNum - 1][col + 1] + 1;
down[rowNum - 1][col] = 1;
}
}
for (int row = rowNum - 2; row >= 0; row--) {
if (m[row][colNum - 1] == 1) {
right[row][colNum - 1] = 1;
down[row][colNum - 1] = down[row + 1][colNum - 1] + 1;
}
}
for (int row = rowNum - 2; row >= 0; row--) {
for (int col = colNum - 2; col >= 0; col--) {
if (m[row][col] == 1) {
right[row][col] = right[row][col + 1] + 1;
down[row][col] = down[row + 1][col] + 1;
}
}
}
}
public static int getMaxSize(int[][] m) {
int[][] right = new int[m.length][m[0].length];
int[][] down = new int[m.length][m[0].length];
setRightAndDown(m, right, down);
for (int length = Math.min(m.length, m[0].length); length >= 1; length--) {
if (hasSizeOfBorder(length, right, down)) {
return length;
}
}
return 0;
}
public static boolean hasSizeOfBorder(int length, int[][] right, int[][] down) {
for (int row = 0; row + length - 1 <= right.length - 1; row++) {
for(int col = 0; col + length - 1 <= right[0].length - 1; col++) {
if (right[row][col] >= length && down[row][col] >= length
&& right[row + length - 1][col] >= length
&& down[row][col + length - 1] >= length) {
return true;
}
}
}
return false;
}
public static int[][] generateRandom01Matrix(int rowSize, int colSize) {
int[][] res = new int[rowSize][colSize];
for (int i = 0; i != rowSize; i++) {
for (int j = 0; j != colSize; j++) {
res[i][j] = (int) (Math.random() * 2);
}
}
return res;
}
public static void printMatrix(int[][] matrix) {
for (int i = 0; i != matrix.length; i++) {
for (int j = 0; j != matrix[0].length; j++) {
System.out.print(matrix[i][j] + " ");
}
System.out.println();
}
}
public static void main(String[] args) {
int[][] matrix = generateRandom01Matrix(7, 8);
printMatrix(matrix);
System.out.println(getMaxSize(matrix));
}
}