问题描述:
- 给定一个由
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]
表示相同的值。
核心思路:
- 纯模拟题,要做的就是分情况讨论,举例子讨论即可。
- 例如
nums = [0, 0, 1, 0, 1, 0, 0, 1, 0]
。 - 可以分成的三数组为
nums1=[0,0,1,0]
、nums2=[1, 0]
和 nums3=[0,1,0]
。 - 关键一:最后一个子数组
nums3
中末尾 0
的个数决定了其他子数组末尾 0
的个数。 - 关键二:最短子数组
nums2 = 2
的长度会决定数组的真正长度。【也就是说 nums1
和 nums3
只需要检验末尾 2
个长度即可知道三个子数组是否在二进制表示上保持一致,因为 nums1
中的前导零是不需要判断的】
- 具体来说:
- 遍历原数组用来维护
1
的索引数组 idx
,用变量 cnt
来记录数组中 1
的个数。 - 当
cnt == 0
,说明数组中全为 0
,则任意返回值都可以。 - 当
cnt
不是 3
的倍数,则说明数组中 1
的个数不能均匀分配,返回 ans = {-1,-1}
即可。 - 处理好前导情况后,
idx[cnt/3]
是第一个数组中最后一个 1
的索引,idx[cnt/3*2]
是第二个数组中最后一个 1
的索引,idx[cnt/3*3]
是第三个数组中最后一个 1
的索引。 - 同时用
zcnt = m-1-idx[cnt]
来记录最后一组中末尾 0
的个数,前面分析的时候已经提过了,最后一组中 0
的个数决定了其他子数组中末尾 0
的个数,因此 idx[cnt/3] + zcnt
就是第一个子数组的尾部。 - 获得三个数组的尾部之后,再获得三个子数组的最小长度
len
,然后三个数组从后往前比较 len
个数字即可知道三个数组是否在二进制表达上一致。
- 该题虽然是困难题,但思路上并不难,只是要处理的情况比较多比较繁琐。
- 上述思路和下述代码均是参考自同一题解,具体看参考内容。
代码实现:
class Solution
{
public:
vector<int> threeEqualParts(vector<int>& arr)
{
int m = arr.size();
vector<int> idx(m+1);
int cnt = 0;
for(int i = 0; i < m; ++i) if(arr[i] == 1) idx[++cnt] = i;
if(cnt == 0) return {0, m-1};
if(cnt % 3 != 0) return {-1, -1};
int gcnt = cnt / 3, zcnt = m-1-idx[cnt];
if(idx[gcnt*2] - idx[gcnt] < zcnt or idx[gcnt*3] - idx[gcnt*2] < zcnt) return {-1,-1};
int tail1 = idx[gcnt]+zcnt;
int tail2 = idx[gcnt*2]+zcnt;
int tail3 = idx[gcnt*3]+zcnt;
int len = min(min(tail1+1, tail2-tail1), tail3-tail2);
vector<int> ans = {tail1, tail2+1};
while(len-- > 0)
{
if(arr[tail1] != arr[tail2] or arr[tail1] != arr[tail3] or arr[tail2] != arr[tail3]) return {-1, -1};
--tail1, --tail2, --tail3;
}
return ans;
}
};
参考内容: