[LeetCode] Split Array with Equal Sum 分割数组成和相同的子数组
Given an array with n integers, you need to find if there are triplets (i, j, k) which satisfies following conditions:
- 0 < i, i + 1 < j, j + 1 < k < n - 1
- Sum of subarrays (0, i - 1), (i + 1, j - 1), (j + 1, k - 1) and (k + 1, n - 1) should be equal.
where we define that subarray (L, R) represents a slice of the original array starting from the element indexed L to the element indexed R.
Example:
Input: [1,2,1,2,1,2,1] Output: True Explanation: i = 1, j = 3, k = 5. sum(0, i - 1) = sum(0, 0) = 1 sum(i + 1, j - 1) = sum(2, 2) = 1 sum(j + 1, k - 1) = sum(4, 4) = 1 sum(k + 1, n - 1) = sum(6, 6) = 1
Note:
- 1 <= n <= 2000.
- Elements in the given array will be in range [-1,000,000, 1,000,000].
class Solution { public: bool splitArray(vector<int>& nums) { if (nums.size() < 7) return false; int n = nums.size(); vector<int> sums = nums; for (int i = 1; i < n; ++i) { sums[i] = sums[i - 1] + nums[i]; } for (int j = 3; j < n - 3; ++j) { unordered_set<int> s; for (int i = 1; i < j - 1; ++i) { if (sums[i - 1] == (sums[j - 1] - sums[i])) { s.insert(sums[i - 1]); } } for (int k = j + 1; k < n - 1; ++k) { int s3 = sums[k - 1] - sums[j], s4 = sums[n - 1] - sums[k]; if (s3 == s4 && s.count(s3)) return true; } } return false; } };
下面这种解法是递归的暴力破解写法,刚开始博主还纳闷了,为啥博主之前写的迭代形式的暴力破解过不了OJ,而这个递归版本的确能通过呢,仔细研究了一下,发现这种解法有两个地方做了优化。第一个优化是在for循环里面,如果i不等于1,且当前数字和之前数字均为0,那么跳过这个位置,因为加上0也不会对target有任何影响,那为什么要加上i不等于1的判断呢,因为输入数组如果是七个0,那么实际上应该返回true的,而如果没有i != 1这个条件限制,后面的代码均不会得到执行,那么就直接返回false了,是不对的。第二个优化的地方是在递归函数里面,只有当curSum等于target了,才进一步调用递归函数,这样就相当于做了剪枝处理,减少了大量的不必要的运算,这可能就是其可以通过OJ的原因吧。
再来说下子函数for循环的终止条件 i < n - 5 + 2*cnt 是怎么得来的,是的,这块的确是个优化,因为i的位置是题目中三个分割点i,j,k的位置,所以其分别有自己可以取值的范围,由于只有三个分割点,所以cnt的取值可以是0,1,2。
-> 当cnt=0时,说明是第一个分割点,那么i < n - 5,表示后面必须最少要留5个数字,因为分割点本身的数字不记入子数组之和,那么所留的五个数字为:数字,第二个分割点,数字,第三个分割点,数字。
-> 当cnt=1时,说明是第二个分割点,那么i < n - 3,表示后面必须最少要留3个数字,因为分割点本身的数字不记入子数组之和,那么所留的三个数字为:数字,第三个分割点,数字。
-> 当cnt=2时,说明是第三个分割点,那么i < n - 1,表示后面必须最少要留1个数字,因为分割点本身的数字不记入子数组之和,那么所留的一个数字为:数字。
解法二:
class Solution { public: bool splitArray(vector<int>& nums) { if (nums.size() < 7) return false; int n = nums.size(), target = 0; int sum = accumulate(nums.begin(), nums.end(), 0); for (int i = 1; i < n - 5; ++i) { if (i != 1 && nums[i] == 0 && nums[i - 1] == 0) continue; target += nums[i - 1]; if (helper(nums, target, sum - target - nums[i], i + 1, 1)) { return true; } } return false; } bool helper(vector<int>& nums, int target, int sum, int start, int cnt) { if (cnt == 3) return sum == target; int curSum = 0, n = nums.size(); for (int i = start + 1; i < n - 5 + 2 * cnt; ++i) { curSum += nums[i - 1]; if (curSum == target && helper(nums, target, sum - curSum - nums[i], i + 1, cnt + 1)) { return true; } } return false; } };
基于上面递归的优化方法的启发,博主将两个优化方法加到了之前写的迭代的暴力破解解法上,就能通过OJ了,perfect!
解法三:
class Solution { public: bool splitArray(vector<int>& nums) { int n = nums.size(); vector<int> sums = nums; for (int i = 1; i < n; ++i) { sums[i] = sums[i - 1] + nums[i]; } for (int i = 1; i <= n - 5; ++i) { if (i != 1 && nums[i] == 0 && nums[i - 1] == 0) continue; for (int j = i + 2; j <= n - 3; ++j) { if (sums[i - 1] != (sums[j - 1] - sums[i])) continue; for (int k = j + 2; k <= n - 1; ++k) { int sum3 = sums[k - 1] - sums[j]; int sum4 = sums[n - 1] - sums[k]; if (sum3 == sum4 && sum3 == sums[i - 1]) { return true; } } } } return false; } };
参考资料:
https://leetcode.com/problems/split-array-with-equal-sum/
https://leetcode.com/problems/split-array-with-equal-sum/discuss/101484/java-solution-dfs
https://leetcode.com/problems/split-array-with-equal-sum/discuss/101481/simple-java-solution-on2