一、问题背景

博主最近在准备2020年的软件工程编程领域的春招,其中华为提供了博主上机考试的机会,因此博主参加了2020年3月25日的华为校园春招。其中博主李同学做出了2020年3月25日华为校园春招真题的第1题和第2题。第2题的题目描述和Java代码实现如下文所示,但第1题的题目描述和Java代码实现请见博主的此篇博客2020年3月25日华为春招真题第1题详解+Java代码实现——2进制与10进制的相互转换。

二、2020年3月25日华为校园春招真题的第2题(最大正方形子矩阵)

1.题目描述

题目描述:输入的第一行是一个数字n,n代表一个矩阵的行数;输入的第2到第n+1行代表一个矩阵的每一行的元素,元素只能是0或1,且每行长度相同。请输出该矩阵中元素全为1的最大正方形子矩阵的面积。输入的矩阵可能是单行矩阵和单列矩阵。

若输入:

3
110
111
110

上述输入对应的输出为:

4

若输入:

8
1010111111
0000000111
1010110111
0000111111
1010111111
0000001111
1010111111
0000110001

上述输入对应的输出为:

16

2.Java代码实现

2.1暴力代码实现(时间复杂度O(n4))

以下代码是博主李同学在考试时慌忙写下的暴力代码实现,尽管写了flag做了优化,但此暴力代码的最坏时间复杂度可达O(n4),因此这种做法是不太可取的,于是博主参考了网上其他大神的做法,即用动态规划可以在时间复杂度为O(n2)的情况下实现题目要求,请看下文的2.2动态规划代码实现

import java.util.Scanner;

public class Problem2 {
    public static void main(String[] args) {
        //正方形最大的长度值
        int maxLength=0;

        //先获得输入数据
        Scanner scanner=new Scanner(System.in);
        int n=scanner.nextInt();
        //用n行字符串存储该矩阵
        String[] strings=new String[n];
        for (int i = 0; i < n; i++) {
            strings[i]=scanner.next();
        }
        //首先判断该矩阵是否是单行或单列矩阵
        //当n为1时,该矩阵为单行矩阵
        if (n==1){
            //若单行矩阵中还包含元素1则输出最大面积1,否则输出最大面积0
            if (strings[0].contains("1")){
                System.out.print(1);
                return;
            }else {
                System.out.print(0);
                return;
            }

        }
        //若矩阵第一行的长度就是1的话,代表该矩阵是单列矩阵
        else if (strings[0].length()==1){
            //若单列矩阵中还包含元素1则输出最大面积1,否则输出最大面积0
            for (int i = 0; i < n; i++) {
                if (strings[i].contains("1")){
                    System.out.print(1);
                    return;
                }
            }
            System.out.println(0);
        }
        //若该矩阵不是单行或单列矩阵,则继续进行一下判断
        else {
            //再找到正方形左上角为1的元素作为查找起点
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < strings[0].length(); j++) {
                    //若某元素是1,则该元素可作为正方形的左上角
                    if (strings[i].charAt(j)=='1'){
                        //根据左上角坐标[i][j]判断,该正方形最多可延伸的长度
                        int maxStretch=Math.min(n-1-i,strings[0].length()-1-j);
                        //那么[i+maxStretch][j+maxStretch]是最多可延伸的正方形的右下角坐标点
                        for (int k = 1; k <= maxStretch; k++) {
                            //若延长后的正方形的右下角坐标值是1,才有讨论该矩阵元素是否全是1的余地
                            if (strings[i+k].charAt(j+k)=='1'){
                                //System.out.println((i+k)+" "+(j+k));
                                //标志该正方形矩阵中是否有0的flag,若flag是0则代表该矩阵中有0
                                int flag=1;
                                for (int l = 0; l <= k; l++) {
                                        //若该行字符串包含一个0则直接结束
                                        if (strings[i+l].substring(j,j+k+1).contains("0")){
                                            flag=0;
                                            break;
                                        }
                                }
                                //矩阵中没有一个0,那么maxLength
                                if (flag==1){
                                    maxLength=Math.max(maxLength,k+1);
                                }
                            }
                            //如果这个正方形的右下角元素为0,那么之后的延展矩阵肯定不会全是1的,所以这里break;直接跳过
                            else {
                                break;
                            }
                        }
                    }
                }
            }
            System.out.print(maxLength*maxLength);
        }
    }
}

2.2动态规划代码实现(时间复杂度O(n2))

import java.util.Scanner;

public class Problem2 {
    public static void main(String[] args) {
        //正方形最大的长度值
        int maxLength=0;
        //先获得输入数据
        Scanner scanner=new Scanner(System.in);
        int n=scanner.nextInt();
        //用n行字符串存储该矩阵
        String[] strings=new String[n];
        for (int i = 0; i < n; i++) {
            strings[i]=scanner.next();
        }
        //建议一个n行strings[0].length()列的二维int数组存储元素
        int[][] dp=new int[n][strings[0].length()];

        //初始化dp的第0列
        for (int i = 0; i < n; i++) {
            //-'0'是因为charAt(0)生成的是char型'0'到'9',它们的Int值是48-58
            dp[i][0]=strings[i].charAt(0)-'0';
            //若该动态规划的元素值是1代表有一个长度为1的正方形
            if (dp[i][0]==1){
                maxLength=1;
            }
        }

        //初始化dp的第0行
        for (int i = 0; i < strings[0].length(); i++) {
            //-'0'是因为charAt(0)生成的是char型'0'到'9',它们的Int值是48-58
            dp[0][i]=strings[0].charAt(i)-'0';
            //若该动态规划的元素值是1代表有一个长度为1的正方形
            if (dp[0][i]==1){
                maxLength=1;
            }
        }

        //从dp的第1行第1列开始,从左往右从上至下计算该dp元素的值,解出dp
        for (int i = 1; i < n; i++) {
            for (int j = 1; j < strings[0].length(); j++) {
                //在原来的二维数组中,若该元素值是1,那么计算该位置的dp值,否则该位置的dp值设为0(dp数组初始化时所有元素为0,所以下面没有设值为0的代码)
                if (strings[i].charAt(j)=='1'){
                    dp[i][j]=Math.min(Math.min(dp[i-1][j-1],dp[i][j-1]),dp[i-1][j])+1;
                    maxLength=maxLength>dp[i][j]?maxLength:dp[i][j];
                }
            }
        }
        System.out.println(maxLength*maxLength);
    }
}

本文参考文献:
[1]Tsai笔记:C++学习随性笔记(5)—— 华为面试题目总结 [1]华为笔试题:根据子网掩码判断两个IP地址是否在同一子网,并输出IP1的网络号 [3]2020年3月25日华为实习生机试编程题 [4]华为机试整理3.25 [5]2020/3/25 华为笔试题