Given an array `A` of `0`s and `1`s, divide the array into 3 non-empty parts such that all of these parts represent the same binary value.

If it is possible, return any [i, j] with i+1 < j, such that:

  • A[0], A[1], ..., A[i] is the first part;
  • A[i+1], A[i+2], ..., A[j-1] is the second part, and
  • A[j], A[j+1], ..., A[A.length - 1] is the third part.
  • All three parts have equal binary value.

If it is not possible, return [-1, -1].

Note that the entire part is used when considering what binary value it represents.  For example, [1,1,0] represents 6 in decimal, not 3.  Also, leading zeros are allowed, so [0,1,1] and [1,1] represent the same value.

Example 1:

Input: [1,0,1,0,1]
Output: [0,3]

Example 2:

Input: [1,1,0,1,1]
Output: [-1,-1]

Note:

  1. 3 <= A.length <= 30000
  2. A[i] == 0 or A[i] == 1

这道题给了我们一个只有0和1的数组,让将其分为三段,使得每一段组成的二进制的数相同,注意数组左边的数字是高位,且开头可能会存在多余的0。最开始博主使用的方法是最直接的暴力方法,遍历所有的分成三段的可能,然后将每一段的二进制都计算出来比较是否相等,结果毫不意外的超时了,丝毫没有尊重这道题的 Hard 标签。仔细分析以下题目,既然要分成三段,且二进制数字相等,那么这三个二进制数中1的个数一定是相等的,因为转十进制的时候,只有1才会累加,而由于开头0的存在,所以0的个数不一定相同。那么1的个数就是突破口了,先遍历整个数组,统计出1的个数。假如数组中没有1的话,那就简单了,随便分三段都行了,因为都是0,所以返回 {0, n-1} 就行。再来想一下,假如1的个数不是3的个数,就绝无可能分为相等值的三段,直接返回 {-1,-1} 即可。假如个数是3的倍数,也不表示一定能成功分为3段,但如果能分的话,每段的1的个数一定是 cntOne/3,现在从末尾开始,找出正好有 cntOnt/3 个的位置 idxThird,此时虽然范围 [idxThird, n-1] 不一定是第三段,因为前面可能有0,但如果能成功分的话,其1的个数及位置一定是正确的。此时要做的是,从开头0开始,略去连续0,然后和 [idxThird, n-1] 区间对比,一旦有不一样的位置,说明无法正确分,返回 {-1,-1}。若能找到相同的一段话,说明此时第一段和第三段存在了,再来检测第二段,此时中间段的1的个数已经确定为 cntOne/3 了,只需要再确定其位置是否一样就可以了,其实主要比的是1的位置,因为开头连续0可以略去,就算每个区间末尾还有0,这些0是可以移动到下一个区间的开头的,从而可以保证对应的1的位置还是一样的,参见代码如下:


解法一:

class Solution {
public:
vector<int> threeEqualParts(vector<int>& A) {
int cntOne = 0, n = A.size();
for (int num : A) {
if (num == 1) ++cntOne;
}
if (cntOne == 0) return {0, n - 1};
if (cntOne % 3 != 0) return {-1, -1};
int idxThird = 0, cnt = 0;
for (int i = n - 1; i >= 0; --i) {
if (A[i] == 0) continue;
++cnt;
if (cnt == cntOne / 3) {
idxThird = i;
break;
}
}
int idx1 = helper(A, 0, idxThird);
if (idx1 < 0) return {-1, -1};
int idx2 = helper(A, idx1 + 1, idxThird);
if (idx2 < 0) return {-1, -1};
return {idx1, idx2 + 1};
}
int helper(vector<int>& A, int left, int right) {
while (A[left] == 0) ++left;
while (right < A.size()) {
if (A[left] != A[right]) return -1;
++left; ++right;
}
return left - 1;
}
};

再来看一种比较类似的方法,开始的操作还是一样的,统计1的个数 cntOne,然后计算出每段的1的个数 k=cntOne/3,再用三个变量 start, mid, end 来标记每段的第一个1的位置,因为想要跳过开头的连续0。再用另一个变量 cnt 来重新在遍历的过程中统计1的个数,在 cnt 为0的时候,一直更新 start,这样可以跳过开头连续0,当 cnt=k+1 时,更新 mid 为i,因为这是第二段中第一个1的位置,当 cnt=2*k+1 时,更新 end 为i,因为这是第三段中第一个1的位置,然后此时验证三个区间,即 A[start],A[mid],和 A[end] 必须相等,然后三个指针同时向后移动一位,若有任何不相等,就 break 掉,最后看若 end 等于n,说明三段区间中1的位置都相等,是符合题意的,返回 {start-1, mid},否则返回 {-1,-1} 即可,参见代码如下:


解法二:

class Solution {
public:
vector<int> threeEqualParts(vector<int>& A) {
int cntOne = 0, n = A.size();
for (int num : A) {
if (num == 1) ++cntOne;
}
if (cntOne == 0) return {0, n - 1};
if (cntOne % 3 != 0) return {-1, -1};
int k = cntOne / 3, start = 0, mid = 0, end = 0, cnt = 0;
for (int i = 0; i < n; ++i) {
if (A[i] == 0) continue;
if (cnt == 0) start = i;
++cnt;
if (cnt == k + 1) mid = i;
if (cnt == 2 * k + 1) {end = i; break;}
}
while (end < n && A[start] == A[mid] && A[mid] == A[end]) {
++start; ++mid; ++end;
}
if (end == n) return {start - 1, mid};
return {-1, -1};
}
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/927

参考资料:

https://leetcode.com/problems/three-equal-parts/

https://leetcode.com/problems/three-equal-parts/discuss/183922/C%2B%2B-O(n)-time-O(1)-space-12-ms-with-explanation-and-comments

https://leetcode.com/problems/three-equal-parts/discuss/223886/Java-O(n)-simple-solution-(don't-know-why-official-solution-is-that-long)

[LeetCode All in One 题目讲解汇总(持续更新中...)](https://www.cnblogs.com/grandyang/p/4606334.html)

[LeetCode] 927. Three Equal Parts 三个相等的部分的更多相关文章

  1. 【leetcode】927. Three Equal Parts

    题目如下: Given an array A of 0s and 1s, divide the array into 3 non-empty parts such that all of these ...

  2. 927. Three Equal Parts

    Given an array A of 0s and 1s, divide the array into 3 non-empty parts such that all of these parts ...

  3. LeetCode:最接近的三数之和【16】

    LeetCode:最接近的三数之和[16] 题目描述 给定一个包括 n 个整数的数组 nums 和 一个目标值 target.找出 nums 中的三个整数,使得它们的和与 target 最接近.返回这 ...

  4. [Swift]LeetCode927. 三等分 | Three Equal Parts

    Given an array A of 0s and 1s, divide the array into 3 non-empty parts such that all of these parts ...

  5. [LintCode/LeetCode]——两数和、三数和、四数和

    LintCode有大部分题目来自LeetCode,但LeetCode比较卡,下面以LintCode为平台,简单介绍我AC的几个题目,并由此引出一些算法基础. 1)两数之和(two-sum) 题目编号: ...

  6. LeetCode 259. 3Sum Smaller (三数之和较小值) $

    Given an array of n integers nums and a target, find the number of index triplets i, j, k with 0 < ...

  7. [LeetCode] 923. 3Sum With Multiplicity 三数之和的多种情况

    Given an integer array A, and an integer target, return the number of tuples i, j, k  such that i &l ...

  8. [LeetCode] 416. Partition Equal Subset Sum 相同子集和分割

    Given a non-empty array containing only positive integers, find if the array can be partitioned into ...

  9. [LeetCode] 16. 3Sum Closest 最近三数之和

    Given an array nums of n integers and an integer target, find three integers in nums such that the s ...

随机推荐

  1. 在linux命令行无界面下,使用selenium进行自动化测试

  2. Java生鲜电商平台-小程序或者APP拼团功能设计与架构实战

    Java生鲜电商平台-小程序或者APP拼团功能设计与架构实战 说明:Java生鲜电商平台拼团是拉新引流的利器,将拼团运用到极致的就是拼多多,前期通过选取性价比高.实用性强的商品进行拼团,在社交圈(主要 ...

  3. Python3---常见函数---range()用法

    0X01函数说明: python range() 函数可创建一个整数列表,一般用在 for 循环中. 0X02函数语法: range(start,stop[,step]) start: 计数从 sta ...

  4. 算法设计与分析 - AC 题目 - 第 2 弹

    PTA-算法设计与分析-AC原题7-1 最大子列和问题 (20分)给定K个整数组成的序列{ N1, N2, ..., NK },“连续子列”被定义为{ Ni, Ni+1, ..., Nj },其中 1 ...

  5. SLT学习——leafes tree扩展 【文艺平衡树】

    这是一个全新的数据结构 md,别看这篇文章了,这篇已经废了. 百折不饶,再交一次,更新复杂度证明 这里是HYF,蒟蒻一只,最近因某些原因开始学数据结构了,然后就写了这篇题解. 下面给大家介绍一个全新的 ...

  6. Django settings源码解析

    Django settings源码 Django中有两个配置文件 局部配置:配置文件settings.py,即项目同名文件夹下的settings.py文件 全局配置:django内部全局的配置文件se ...

  7. 【转载】Git设置单个文件上传大小

    git单个文件默认大小是50M,超过50M,会给出warning.大于100M会无法提交: 可以通过命令,修改单个文件默认大小(以设置500M以例): git config --global http ...

  8. Linux centosVMware 命令 lvm、磁盘故障小案例

    一.lvm命令 LVM:逻辑分区管理,可基于动态的扩展缩小硬件设备的使用空间,注意:lvm磁盘复杂,由于使用lvm,数据丢失恢复起来有一定风险.概念:pv.VG.lvpv(物理卷,有pp基本单位构成) ...

  9. Activemq、Rabbitmq、Rocketmq、Kafka的对比

    综上所述,各种对比之后,我个人倾向于是: 一般的业务系统要引入MQ,最早大家都用ActiveMQ,但是现在确实大家用的不多了,没经过大规模吞吐量场景的验证,社区也不是很活跃,所以大家还是算了吧,我个人 ...

  10. 2017北京网络赛 J Pangu and Stones 区间DP(石子归并)

    #1636 : Pangu and Stones 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 In Chinese mythology, Pangu is the fi ...