方法1:左神的暴力法和预处里数组检查法
class Solution {
//数组预处理法,大幅度加快速度
public int largest1BorderedSquare(int[][] grid) {
if(grid.length==0||grid[0].length==0){
return 0;
}
int[][] right=new int[grid.length][grid[0].length];
int[][] down=new int[grid.length][grid[0].length];
initPA(right,down,grid);
int chang=grid[0].length;
int height=grid.length;
for(int k=Math.min(grid.length,grid[0].length);k>0;k--){//从最大边开始找,找到就返回k*k
for(int i=0;i+k-1<height;i++){
for(int j=0;j+k-1<chang;j++){
//检查边界是否都是1;
if(check(i,j,k,right,down)){
return k*k;
}
}
}
}
return 0;
}
public boolean check(int i,int j,int k,int[][] right,int[][] down){
if(right[i][j]>=k&&down[i][j]>=k&&right[i+k-1][j]>=k&&down[i][j+k-1]>=k){
return true;
}
return false;
}
public void initPA(int[][] right,int[][] down,int[][] grid){ //init prepare array
//初始化right数组,从第一行最后边开始初始化
for(int i=0;i<right.length;i++){
for(int j=right[0].length-1;j>=0;j--){
if(j==right[0].length-1&&grid[i][j]==1){
right[i][j]=1;
}else if(j!=right[0].length-1&&grid[i][j]==1){
right[i][j]=right[i][j+1]+1;
}
}
}
//初始化down数组
for(int j=0;j<down[0].length;j++){
for(int i=down.length-1;i>=0;i--){
if(i==down.length-1&&grid[i][j]==1){
down[i][j]=1;
}else if(i!=down.length-1&&grid[i][j]==1){
down[i][j]=down[i+1][j]+1;
}
}
}
}
//暴力检查法
/**
*有个注意的地方,就是边长,如只有1.这也是正方形,表示边长为1的正方形,所以for里面是i+k-1,因为这样才是边长
*/
public int largest1BorderedSquare1(int[][] grid) {
if(grid.length==0||grid[0].length==0){
return 0;
}
int chang=grid[0].length;
int height=grid.length;
for(int k=Math.min(grid.length,grid[0].length);k>0;k--){//从最大边开始找,找到就返回k*k
for(int i=0;i+k-1<height;i++){
for(int j=0;j+k-1<chang;j++){
//检查边界是否都是1;
if(check1(i,j,k,grid)){
return k*k;
}
}
}
}
return 0;
}
public boolean check1(int i,int j,int k,int[][] grid){
//一共检查四条边,第一条是第一横边,第二横边,第1戍边,第二戍边
//但是可以发现,两个横边可以放在一起检查,两个戍边也可以放在一起检查,所以只需要两个for循环
//首先检查横边
for(int m=j;m<=j+k-1;m++){
if(grid[i][m]!=1||grid[i+k-1][m]!=1){
return false;
}
}
//检查戍边
for(int m=i;m<=i+k-1;m++){
if(grid[m][j]!=1||grid[m][j+k-1]!=1){
return false;
}
}
return true;
}
}
还有,例如求一个数组0-i位置的最大值,你可以这样:就是新建一个数组,然后遍历原数组,从头遍历,如果当前数小于前一个,就记录前一个,这样:
1 2 0 2 3 0 5-》
1 2 2 2 3 3 5 就这个意思,我的表达能力有问题。。。表达不清楚
方法2:leetcode大神方法,我搞不懂,暴力法是29秒,优化之后是13秒,但是这个和优化的时间复杂度只差在常数项,但是他是5秒,这个快的很多
没有去操作呢,回头看
class Solution {
public int largest1BorderedSquare(int[][] grid) {
if (grid.length==0) return 0;
int[][] dpr = new int[grid.length+1][grid[0].length+1];
int[][] dpc = new int[grid.length+1][grid[0].length+1];
int dist, max=0;
for (int r=1;r<=grid.length;r++){
for (int c=1;c<=grid[0].length;c++){
if (grid[r-1][c-1]==0) continue;
dpr[r][c] = dpr[r-1][c]+1;
dpc[r][c] = dpc[r][c-1]+1;
dist = Math.min(dpr[r][c],dpc[r][c]);
for (int i=dist;i>=1;i--){
**if (dpr[r][c-i+1]>=i**
&& dpc[r-i+1][c]>=i){
max = Math.max(max, i*i);
break;
}
}
}
}
return max;
}
}
补丁:用的是leetcode大神思想做的,除了数组基准不同,其他的都一样,因为习惯我喜欢用原数组作为基准,他的思想就是从头到尾遍历数组,每一个点当做的是右下角的顶点,他有个处理非常好,就是left和up数组比grid数组要大一些,这样就不用初始化两条边了,代码简洁了,牛逼
class Solution {
public int largest1BorderedSquare(int[][] grid) {
if(grid.length==0||grid[0].length==0){
return 0;
}
//他找的是右下角的点,以前我的做法是左上角,不一样的
int[][] left=new int[grid.length+1][grid[0].length+1];
int[][] up=new int[grid.length+1][grid[0].length+1];
int max=0;
//这个比grid大了一些,但是就是这些,使得后续的处理方便了,因为他相当于初始化了第一行和第一列,否则,还要
//分支处理,加判断
//leetcode大神,是用的left和up当基准的(更方便但是理解性差一点,所以我才用的是grid当做标准的)
for(int i=0;i<grid.length;i++){
for(int j=0;j<grid[0].length;j++){
//每次到一个位置,先更新left和up数组
if(grid[i][j]==0){
continue;//当前为0的不做任何判断,也不作任何处理
}
left[i+1][j+1]=left[i+1][j]+1;
up[i+1][j+1]=up[i][j+1]+1;
int k=Math.min(left[i+1][j+1],up[i+1][j+1]);//以当前为右下角顶点最大边长。主要是减少比较次数的,要不然你岂不是遍历很多次
for(int dist=k;dist>0;dist--){
if(up[i+1][j+1-dist+1]>=dist&&left[i+1-dist+1][j+1]>=dist){
max=Math.max(max,dist);
break;
}
}
}
}
return max*max;
}
}