刷题的日常-三等分

70 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第9天,点击查看活动详情

刷题的日常-2022年10月6号

一天一题,保持脑子清爽

三等分

来自leetcode的 927 题,题意如下:

给定一个由 0 和 1 组成的数组arr,将数组分成 3个非空的部分 ,使得所有这些部分表示相同的二进制值。
如果可以做到,请返回任何[i, j],其中 i+1 < j,这样一来:

  • arr[0], arr[1], ..., arr[i]为第一部分;
  • arr[i + 1], arr[i + 2], ..., arr[j - 1]为第二部分;
  • arr[j], arr[j + 1], ..., arr[arr.length - 1]为第三部分。
  • 这三个部分所表示的二进制值相等。

如果无法做到,就返回[-1, -1]。
注意,在考虑每个部分所表示的二进制时,应当将其看作一个整体。例如,[1,1,0]表示十进制中的6,而不会是3。此外,前导零也是被允许的,所以[0,1,1] 和[1,1]表示相同的值。

理解题意

我们可以从题意中提取的条件如下:

  • 题目给出一个由 0 1 组成的数组
  • 要求我们返回将数组进行三等分
  • 要求每个等分的部分的二进制表示都一致

做题思路

从题意中可以发现,如果三等分要能够匹配,那么它们的1的个数一定要一致,所以1的总数要能被3整除。还有的是,前导零对我们没影响,但是后导零是有的。仔细查看可以发现,最后一部分的后导零是没办法消除的,所以找到最后一位开始的位置,我们就可以知道分割出来的数应该是什么样子的。步骤如下:

  • 统计1的个数
  • 如果不能被3整除,直接返回。需要注意的是,如果全是0,那么怎么分割都是对的,这里直接返回前后指针就可以了
  • 查找每部分的开始位置
  • 由最后一部分往后扫描,如果全部匹配,就返回最后的索引位置
  • 不能匹配则返回-1

代码实现

代码实现如下:

public class Solution {
    public int[] threeEqualParts(int... arr) {
        int[] idx = new int[]{-1, -1, -1};
        int len = 0, cnt = 0, dev = -1;
        for (int num : arr) {
            len += num;
        }
        if (len == 0) {
            return new int[]{0, arr.length - 1};
        }
        if (len % 3 != 0) {
            return new int[]{-1, -1};
        }
        len /= 3;
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] == 0) {
                continue;
            }
            cnt += arr[i];
            if (cnt == 1) {
                dev++;
            }
            cnt %= len;
            idx[dev] = idx[dev] == -1 ? i : idx[dev];
            if (dev == 2) {
                break;
            }
        }
        while (idx[2] < arr.length) {
            if (arr[idx[0]] != arr[idx[1]] || arr[idx[0]] != arr[idx[2]]) {
                return new int[]{-1, -1};
            }
            idx[0]++;
            idx[1]++;
            idx[2]++;
        }
        return new int[]{idx[0] - 1, idx[1]};
    }
}

image.png