[LeetCode] 923. 3Sum With Multiplicity
Given an integer array arr
, and an integer target
, return the number of tuples i, j, k
such that i < j < k
and arr[i] + arr[j] + arr[k] == target
.
As the answer can be very large, return it modulo 109 + 7
.
Example 1:
Input: arr = [1,1,2,2,3,3,4,4,5,5], target = 8 Output: 20 Explanation: Enumerating by the values (arr[i], arr[j], arr[k]): (1, 2, 5) occurs 8 times; (1, 3, 4) occurs 8 times; (2, 2, 4) occurs 2 times; (2, 3, 3) occurs 2 times.
Example 2:
Input: arr = [1,1,2,2,2,2], target = 5 Output: 12 Explanation: arr[i] = 1, arr[j] = arr[k] = 2 occurs 12 times: We choose one 1 from [1,1] in 2 ways, and two 2s from [2,2,2,2] in 6 ways.
Example 3:
Input: arr = [2,1,3], target = 6 Output: 1 Explanation: (1, 2, 3) occured one time in the array so we return 1.
Constraints:
3 <= arr.length <= 3000
0 <= arr[i] <= 100
0 <= target <= 300
三数之和的多种可能。
给定一个整数数组 arr ,以及一个整数 target 作为目标值,返回满足 i < j < k 且 arr[i] + arr[j] + arr[k] == target 的元组 i, j, k 的数量。
由于结果会非常大,请返回 109 + 7 的模。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/3sum-with-multiplicity
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这道题我们找的是一个满足题意的三元组,但是注意因为input里面给的数字是有重复的,所以计算的时候要小心一些。因为题目要求三个数的下标要满足 i < j < k,这道题的思路是偏向桶排序,也会应用到一些 two sum 的思路。
首先我们记录一下 input 数组里面每个数字都分别出现的次数,用一个 count 数组记录好。接着我们还是按 3 sum 的思路开始遍历,首先确定前两个数字 i, j(注意这里的 i, j, k 其实不是下标,题目说的不清楚,这里其实指的是桶排序里三个不同的位置,指向的是三个不同的数字),那么要找的第三个数字 k = target - i - j。如果 k 的范围不在 0 - 100 则说明是一个无效的组合(因为题目的数据范围规定 0 <= arr[i] <= 100),直接跳过即可;当 k 是一个有效的数字的时候,也需要判断如下几种 case
- 如果 i == j && j == k,说明 i, j, k 三个数字相同。对于三个数字相同的组合,他们可以产生的组合数量是 count[i] * (count[i] - 1) * (count[i] - 2) / 6
- 如果 i == j && j != k, 说明其中两个数字相同,组合数量是 count[i] * (count[i] - 1) / 2 * count[k]
- 如果三个数字都互不相同,组合数量是 count[i] * count[j] * count[k]
这个做法巧妙之处在于他扫描的不是原数组,而是根据题目给的数据范围去扫描(0 - 100)。比如第一个例子的 target = 8,input数组里有 1 和 2,假设此时没有 5 的话,根据我们的算法,count[5] == 0,所以 1 + 2 + 5 = 8 这个组合也不会找的到。
时间O(n^2)
空间O(n)
Java实现
1 class Solution { 2 public int threeSumMulti(int[] arr, int target) { 3 long[] count = new long[101]; 4 for (int num : arr) { 5 count[num]++; 6 } 7 8 int MOD = (int) Math.pow(10, 9) + 7; 9 // 注意res的类型,极容易错 10 long res = 0; 11 for (int i = 0; i <= 100; i++) { 12 for (int j = i; j <= 100; j++) { 13 int k = target - i - j; 14 if (k > 100 || k < 0) { 15 continue; 16 } 17 // 三个数相同 18 if (i == j && j == k) { 19 res += count[i] * (count[i] - 1) * (count[i] - 2) / 6; 20 } 21 // 其中两个数相同 22 else if (i == j && j != k) { 23 res += count[i] * (count[i] - 1) / 2 * count[k]; 24 } 25 // 三个数各不相同 26 else if (i < j && j < k) { 27 res += count[i] * count[j] * count[k]; 28 } 29 } 30 } 31 return (int) (res % MOD); 32 } 33 }