Given a 0-indexed integer array nums
, return the number of distinct quadruplets (a, b, c, d)
such that:
nums[a] + nums[b] + nums[c] == nums[d]
, anda < b < c < d
Example 1:
Input: nums = [1,2,3,6] Output: 1 Explanation: The only quadruplet that satisfies the requirement is (0, 1, 2, 3) because 1 + 2 + 3 == 6.
Example 2:
Input: nums = [3,3,6,4,5] Output: 0 Explanation: There are no such quadruplets in [3,3,6,4,5].
Example 3:
Input: nums = [1,1,1,3,5] Output: 4 Explanation: The 4 quadruplets that satisfy the requirement are: - (0, 1, 2, 3): 1 + 1 + 1 == 3 - (0, 1, 3, 4): 1 + 1 + 3 == 5 - (0, 2, 3, 4): 1 + 1 + 3 == 5 - (1, 2, 3, 4): 1 + 1 + 3 == 5
Constraints:
4 <= nums.length <= 50
1 <= nums[i] <= 100
这题是求数组里面任意三个数字加起来之和等于第四个数的组合个数,其实kind of like 3sum,但是因为给定的sum也是数组里的一个数,所以相当于得再加一层时间复杂度。
最简单的方法就是四层for loop,显然这里并不想用brute force,肯定有更好的方法。借鉴2sum,我们可以固定一个数,放进hashmap里,然后剩下三层for loop里用hashmap查找,就可以省一层时间复杂度。但是这也不是最优的方法,最优的方法可以达到O(n^2),只用两层for loop。
考虑到nums[a] + nums[b] + nums[c] = nums[d],我们可以把这个等式变成nums[a] + nums[b] = nums[d] - nums[c]。那么我们就需要固定a + b,或者d - c。假如我们可以enumerate所有d - c的可能性,并把它存进一个diff(d, c)到这个diff的出现次数的map,那么就可以简化这道题成类似于2sum的形式,通过固定b来寻找a,达到O(n^2)的时间复杂度。
由于题目给定了abcd从小到大排序,那么我们就对b的位置进行for loop,把数组拆成两半,一半是a到b,另一半是c到d。因为b后面还有c和d,前面还有a,所以b的范围是[1, len - 3],那么a就是[0, b - 1]。对a进行遍历时,就只需要从map里找有没有目标数字(a + b)就行。而对于(c, d) pair,这里有一点小技巧which我花了很长时间才理解。因为我们是对b进行固定,每次往前移一个,那么其实相当于这一轮的b就是下一轮的c,从后往前遍历可以让我们每次新生成的(c, d) pair只增加新的c和它后面每一个数字组成的pair,达到O(n)的效果。所以在for loop外面我们得先放进去最后两个作为(c, d) pair,相当于在for loop里面我们提前算好下一轮的(c, d) pair的map。具体代码如下,顺便也贴个reference,以防以后又看不懂自己在写什么了。
reference:LeetCode - The World's Leading Online Programming Learning Platform
class Solution {
public int countQuadruplets(int[] nums) {
// nums[a] + nums[b] = nums[d] - nums[c]
int result = 0;
Map<Integer, Integer> diffToCount = new HashMap<>();
// need to count for the last pair
diffToCount.put(nums[nums.length - 1] - nums[nums.length - 2], 1);
for (int b = nums.length - 3; b >= 1; b--) {
for (int a = 0; a < b; a++) {
result += diffToCount.getOrDefault(nums[a] + nums[b], 0);
}
// nums[b] can be used as nums[c]
// it is just preparation for next iteration of outside loop
// as it is the last step of an iteration -> current b is next round's c
for (int d = b + 1; d < nums.length; d++) {
diffToCount.put(nums[d] - nums[b], diffToCount.getOrDefault(nums[d] - nums[b], 0) + 1);
}
}
return result;
}
}