[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 数组里面有重复的数字,所以我们需要记录每个数字出现的次数。

首先我们记录一下 input 数组里面每个数字都分别出现的次数,用一个 count 数组记录好。接着我们还是按 3sum 的思路开始遍历,首先确定前两个数字 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Solution {
public int threeSumMulti(int[] arr, int target) {
long[] count = new long[101];
for (int num : arr) {
count[num]++;
}

int MOD = (int) Math.pow(10, 9) + 7;
// 注意res的类型,极容易错
long res = 0;
for (int i = 0; i <= 100; i++) {
for (int j = i; j <= 100; j++) {
int k = target - i - j;
if (k > 100 || k < 0) {
continue;
}
// 三个数相同
if (i == j && j == k) {
res += count[i] * (count[i] - 1) * (count[i] - 2) / 6;
}
// 其中两个数相同
else if (i == j && j != k) {
res += count[i] * (count[i] - 1) / 2 * count[k];
}
// 三个数各不相同
else if (i < j && j < k) {
res += count[i] * count[j] * count[k];
}
}
}
return (int) (res % MOD);
}
}

[LeetCode] 923. 3Sum With Multiplicity
https://shurui91.github.io/posts/269569957.html
Author
Aaron Liu
Posted on
February 5, 2021
Licensed under