Halo

A magic place for coding

0%

LeetCode 解题报告(325)-- 923. 3Sum With Multiplicity

Problem

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:

1
2
3
4
5
6
7
8
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:

1
2
3
4
5
6
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.

Constraints:

  • 3 <= arr.length <= 3000
  • 0 <= arr [i] <= 100
  • 0 <= target <= 300

Analysis

   题目是 3 sum 的改进版,最原始的 3 sum 的数组中的数字是唯一的,而这里数组中的数字是不唯一的,题目要求找出能够构成目标值的三个数字的组合的数量。所以这道题目可以分成两个部分,第一个是找出三个数加起来为目标值,第二个是把组合的数量计算出来。

   第一部分好做,就是 3 sum 的做法。先确定一个数组,然后有左右两个下标来找出剩下的两个数,这个比较简单,这里不再赘述。

   难的是第二部分,如何求出满足条件的组合数量。如果不存在重复的数字,那么找到的满足题目条件的三元组数量就是答案的数量。这里要解决的是重复数字的问题,其实也并不难。因为我们在处理第一部分的时候,已经把数组排好序,所以重复的数字是相邻出现的,因此在找到剩下的两个数字后(分别是 arr [j]arr [k]),我们找与这两个数字相同的数分别有多少个。j 往右找,假设找到了 left 个;k 往左找,假设找到了 right 个。这样就知道每个数字各有多少个,相互组合即可。


Solution

   这里有个点需要注意,找到重复数组组合时,要分情况处理:

  1. arr [j] != arr [k] 时,组合数量就是 $left \times right$;
  2. arr [j] == arr [k] 时,说明这两个数字是一样的,所以 k - j 就是这个数字出现的数量,而计算组合时,我们需要从中抽取两个出来,利用组合数公式,此时组合的数量就是 $\frac {(k - j + 1) \times (k - j)}{2}$。

Code

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
class Solution {
public:
int threeSumMulti(vector<int>& arr, int target) {
long result = 0, size = arr.size (), M = 1e9 + 7;
sort (arr.begin (), arr.end ());
for (int i = 0; i < size - 2; i++) {
int sum = target - arr [i];

int j = i + 1, k = size - 1; //search window [i + 1, size - 1]
while (j < k) {
if (arr [j] + arr [k] < sum) {
j++;
} else if (arr [j] + arr [k] > sum) {
k--;
} else {
int left = 1, right = 1;
while (j + left < k && arr [j + left] == arr [j]) {
left++;
}
while (j + left <= k - right && arr [k - right] == arr [k]) {
right++;
}
result += arr [j] == arr [k] ? (k - j + 1) * (k - j) / 2: left * right;
j += left;
k -= right;
}
}
}
return result % M;
}
};

Summary

   这道题目是属于 3sum 的一个变种,找三元组的方法是一样的,增加的难点在于如何处理重复数字带来的组合问题。同时还要特别注意相同数组的组合计算问题。这道题目的分享到这里,感谢你的支持!

Welcome to my other publishing channels