题目来源:https://leetcode.cn/problems/three-equal-parts/
大致题意:
给一个 0 和 1 组成的数组 arr,求能否将数组分成三个连续的子数组 arr[0] ~ arr[i]、arr[i + 1] ~ arr[j - 1] 和 arr[j] ~ arr[n - 1],使得子数组表示的二进制数都相等
如果可以则返回分界的 [i, j],不可以则返回 [-1, -1]
思路
如果三个子数组表示的二进制相同,则它们一定有相同个数的 1
于是首先判断数组中 1 的个数,如果为 0,则表示任意划分子数组都满足条件;如果不为 3 的倍数,则一定无法划分成满足条件的子数组,直接返回结果
当 1 的个数为 3 的倍数时,就需要确定边界。假设,1 的个数为 count,每个子数组中 1 的个数为 partition。那么可知在给定 1 的个数后,最后一段分割的子数组表示的二进制数是固定的,证明如下
- 最后一段子数组的 1 为第 2 * partition + 1 个 1 到第 count 个 1
- 设第 2 * partition + 1 个 1 的索引为 x,那么分界的 j 一定小于等于 x
- 理论上说分界位置的不同会导致二进制数不同,但是限制了 1 的个数后,索引 j 至 索引 x 的位置,除了 x 位置本身是 1 以外,其他位置都是 0。而在二进制数前面加 0 是不影响值的
所以算出第 2 * partition + 1 个 1 的位置后,即可得到一个二进制数,然后可以分割子数组,再算出第 1 个和第 partition + 1 个 1 的位置,那么以这三个位置开始的二进制应该是相等的,如果不相等则表示无法划分成满足条件的子数组,直接返回结果
那么解题思路可以概括为
- 算出数组中 1 的个数,如果为 0,则表示任意划分子数组都满足条件;如果不为 3 的倍数,则一定无法划分成满足条件的子数组,直接返回 [-1, -1];否则进入下一步
- 算出每个子数组中 1 的个数为 partition,那么分界的 [i, j] 应为 [第 partition + 1 个 1 的位置,第 2 * partition + 1 个 1 的位置],获取每个子数组开始位置
- 开始遍历三个子数组,如果有一位不是完全相同,则直接返回 [-1, -1];否则进入下一步
- 返回子数组的分界值
代码:
class Solution {
public int[] threeEqualParts(int[] arr) {
int n = arr.length;
int count = Arrays.stream(arr).sum();
// 1 的个数不为 3 的倍数,不能三等分
if (count % 3 != 0) {
return new int[]{-1, -1};
}
// 1 的个数为 0,返回任意两个索引值
if (count == 0) {
return new int[]{0, n - 1};
}
// 每个部分 1 的个数
int partition = count / 3;
// 统计第 1 个 1 出现的位置
// 第 parttion + 1 个 1 出现的位置
// 第 2 * partition + 1 个 1 出现的位置
int first = -1;
int second = -1;
int third = -1;
int cur = 0;
for (int i = 0; i < n; i++) {
if (arr[i] == 1) {
if (cur == 0) { // 第 1 个 1 出现的位置
first = i;
}
if (cur == partition) { // 第 parttion + 1 个 1 出现的位置
second = i;
}
if (cur == partition * 2) { // 第 2 * partition + 1 个 1 出现的位置
third = i;
}
cur++;
}
}
// 获取每部分的最短长度
int len = n - third;
// 如果中间两部分的长度小于最短长度,则对应二进制数肯定不同
if (first + len > second || second + len > third) {
return new int[]{-1, -1};
}
int idx = 0;
// 遍历判断对应二进制是否相同
while (third + idx < n) {
if (arr[third + idx] != arr[first + idx] || arr[third + idx] != arr[second + idx]) {
return new int[]{-1, -1};
}
idx++;
}
return new int[]{first + len - 1, second + len};
}
}