2023-03-16:给定一个由 0 和 1 组成的数组 arr ,将数组分成 3 个非空的部分, 使得所有这些部分表示相同的二进制值。 如果可以做到,请返回任何 [i, j],其中 i+1 < j
2023-03-16:给定一个由 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] 表示相同的值。
输入:arr = [1,0,1,0,1],
输出:[0,3]。
输入:arr = [1,1,0,0,1],
输出:[0,2]。
答案2023-03-16:
给定一个由 0 和 1 组成的数组 arr,需要将其分成三个非空部分,使得每个部分中 1 的数量相等。如果无法做到,则返回 [-1, -1]。
输入:由 0 和 1 组成的数组 arr,长度为 n(1 ≤ n ≤ 3×10^4),且只包含数字 0 和 1。
输出:长度为 2 的数组,表示能够将 arr 分成三个部分时第一个和第二个部分的结束位置(下标从 0 开始)。如果无法做到则返回 [-1, -1]。
解法思路:
首先统计整个数组中 1 的数量 ones,如果 ones 不能被 3 整除,则说明无法分成三个相等的部分,直接返回 [-1, -1]。
如果 ones 等于 0,则整个数组都是 0,可以返回 [0, n-1]。
接着需要找到第一个、第二个和第三个部分的起始位置。根据题意,第一个部分和第二个部分的 1 的数量应该是 ones/3,因此可以先计算出目标值 part = ones/3,然后从左到右遍历整个数组,在找到第一个和第二个部分之后,继续遍历找到第三个部分的起始位置。
接下来检查第三个部分是否也等于目标值 part。如果是,则返回 [end1, end2],否则返回 [-1, -1]。
代码实现:
fn main() {
let arr1 = vec![0, 0, 0, 0, 0];
println!("{:?}", three_equal_parts(arr1)); // [0, 4]
let arr2 = vec![1, 0, 1, 0, 1, 0];
println!("{:?}", three_equal_parts(arr2)); // [1, 4]
let arr3 = vec![1, 0, 1, 0, 1];
println!("{:?}", three_equal_parts(arr3)); // [0, 3]
let arr4 = vec![1, 0, 1, 1, 1];
println!("{:?}", three_equal_parts(arr4)); // [-1, -1]
let arr5 = vec![0, 1, 0, 1, 0, 1, 0, 1, 0, 1];
println!("{:?}", three_equal_parts(arr5)); // [-1, -1]
let arr6 = vec![1, 1, 0, 1, 1, 0, 1, 1];
println!("{:?}", three_equal_parts(arr6)); // [1, 5]
}
pub fn three_equal_parts(arr: Vec<i32>) -> Vec<i32> {
let ones = arr.iter().filter(|&num| *num == 1).count(); // 统计数组中 1 的个数
if ones % 3 != 0 {
// 如果无法分成三个相等的部分,则返回 [-1, -1]
return vec![-1, -1];
}
let n = arr.len();
if ones == 0 {
// 如果整个数组都是 0,则返回 [0, n-1]
return vec![0, n as i32 - 1];
}
let part = ones / 3; // 计算每个子数组中 1 的数量
let mut start1 = -1; // 第一个子数组的起始位置
let mut start2 = -1; // 第二个子数组的起始位置
let mut start3 = -1; // 第三个子数组的起始位置
let mut cnt = 0; // 当前已经遇到的 1 的数量
for i in 0..n {
if arr[i] == 1 {
cnt += 1;
if start1 == -1 && cnt == 1 {
start1 = i as i32; // 找到第一个子数组的起始位置
}
if start2 == -1 && cnt == part + 1 {
start2 = i as i32; // 找到第二个子数组的起始位置
}
if start3 == -1 && cnt == 2 * part + 1 {
start3 = i as i32; // 找到第三个子数组的起始位置
}
}
}
while start3 < n as i32 {
if arr[start1 as usize] != arr[start2 as usize]
|| arr[start1 as usize] != arr[start3 as usize]
{
return vec![-1, -1]; // 如果找到的三个子数组不相等,则返回 [-1, -1]
}
start1 += 1;
start2 += 1;
start3 += 1;
}
vec![start1 - 1, start2] // 返回第一个和第二个子数组的结束位置
}
算法分析:
该算法的时间复杂度为 O(n),其中 n 是输入数组的长度,因为需要遍历整个数组一次。空间复杂度为 O(1),只需要常量级别的额外空间存储一些变量。该算法的优点是简单易懂,缺点是可能会超时,比如当输入数组中有很多连续的 1 时。可以通过进一步优化算法来提高效率。
测试结果:
1.测试用例:[0,0,0,0,0],预期输出:[0, 4]。
assert_eq!(three_equal_parts(vec![0,0,0,0,0]), vec![0, 4]);
2.测试用例:[1, 0, 1, 0, 1, 0],预期输出:[1, 4]。
assert_eq!(three_equal_parts(vec![1, 0, 1, 0, 1, 0]), vec![1, 4]);
3.测试用例:[1, 0, 1, 0, 1],预期输出:[0, 3]。
assert_eq!(three_equal_parts(vec![1, 0, 1, 0, 1]), vec![0, 3]);
4.测试用例:[1, 0, 1, 1, 1],预期输出:[-1, -1]。
assert_eq!(three_equal_parts(vec![1, 0, 1, 1, 1]), vec![-1, -1]);
5.测试用例:[0, 1, 0, 1, 0, 1, 0, 1, 0, 1],预期输出:[-1, -1]。
assert_eq!(three_equal_parts(vec![0, 1, 0, 1, 0, 1, 0, 1, 0, 1]), vec![-1, -1]);
6.测试用例:[1, 1, 0, 1, 1, 0, 1, 1],预期输出:[1, 5]。
assert_eq!(three_equal_parts(vec![1, 1, 0, 1, 1, 0, 1, 1]), vec![1, 5]);
总结和展望:
本文介绍了一种简单的算法,可以解决给定一个由 0 和 1 组成的数组 arr,需将其分成三个非空部分,使得每个部分中 1 的数量相等的问题。该算法的核心思路是计算目标值 target_val,并在遍历整个数组两次的过程中找到第一个和第二个部分的结束位置 i 和 j。该算法的时间复杂度为 O(n),空间复杂度为 O(1)。
有一些情况下该算法可能会超时,比如当输入数组中有很多连续的 1 时。可以通过进一步优化算法来提高效率。例如,可以使用双指针来记录第一个和第二个部分的结束位置,从而减少遍历数组的次数。另外,可以使用位运算来加速计算当前部分的二进制数值。