字节跳动竞赛算法-统计全1子矩阵

力扣X字节跳动测试-统计全1子矩阵
字节跳动竞赛算法-统计全1子矩阵_第1张图片

这道题做了一个小时没有做出来,想用动态规划,对于只有单行或者单列的情况,还是很好解决的,但是碰到多行多列的情况,就不知道该怎么处理了,还是太菜了,好好学习一下别人的做法吧。

使用与运算符

采用逐行分析法:
对于只有一行数组而言,比如[1,1,1,0,1,1,1,1,0,1]
前3个1,能组成 3个 1x1, 2个 1x2, 1个 1x3,一共3+2+1=6种情况
中间4个1,能组成4个 1x1, 3个 1x2, 2个 1x3,1个 1x4,一共4+3+2+1=10种情况
所以能找到一种规律,连续出现的n个1,可以组成 1 + 2 + 3 + … + n 种情况,使用公式,就是 n * (n + 1) / 2 种情况

上面是只有一行的情况。
对于2行而言,比如
字节跳动竞赛算法-统计全1子矩阵_第2张图片
只有第3列能组成 2x1, 最后两列能组成 1个2x2,2个2x1,
字节跳动竞赛算法-统计全1子矩阵_第3张图片
我们可以发现,只有在同一列,全部都是1时才能起到作用,对于其他没被框住的地方,1的存在是没有意义的,这里的没有意义指的是,把第一行也考虑进去的前提下,第二行第5列的1是没有意义的,因为和第一行第5列不能凑成全1。这个结果和只有1行的[0, 0, 1 ,0, 0, 0 ,0 , 1, 1]的结果是一样的。
2行变成1行,通过观察可以发现,可以使用与运算 & 实现。

所以整个遍历顺序是这样的:

①先遍历当前行(R),看能凑成多少种方法;
②将当前行(R)与下面一行(R+1)进行与操作,得到nums[],计算该nums[]能凑成多少种方法,进行累加;将结果nums[]再和下下行(R+2)进行与操作,进行累加…直到所有行合并成一行,这次累加结束。
③将R+1行当做当前行,重复①~③,直到所有行计算完成。

代码如下:

class Solution {
    public int numSubmat(int[][] mat) {
        int result = 0;
        for (int i = 0; i < mat.length; i++) {
            int[] nums = new int[mat[i].length];
            System.arraycopy(mat[i], 0, nums, 0, mat[i].length);

            result += getRes(nums);

            for (int j = i + 1; j < mat.length; j++) {
                for (int k = 0; k < mat[j].length; ++k) {
                    nums[k] = nums[k] & mat[j][k];
                }
                result += getRes(nums);
            }
        }
        return result;
    }

    //计算1行
    public int getRes(int[] nums) {
        int result = 0;     //结果
        int continuous = 0;  //连续出现的1的个数
        for (int num : nums) {
            if (num == 0) {
                result += continuous * (continuous + 1) / 2;
                continuous = 0;
            } else {
                continuous++;
            }
        }
        result += continuous * (continuous + 1) / 2;
        return result;
    }
}

你可能感兴趣的:(算法)