|
| 1 | +# [0923. 三数之和的多种可能](https://leetcode.cn/problems/3sum-with-multiplicity/) |
| 2 | + |
| 3 | +- 标签:数组、哈希表、双指针、计数、排序 |
| 4 | +- 难度:中等 |
| 5 | + |
| 6 | +## 题目链接 |
| 7 | + |
| 8 | +- [0923. 三数之和的多种可能 - 力扣](https://leetcode.cn/problems/3sum-with-multiplicity/) |
| 9 | + |
| 10 | +## 题目大意 |
| 11 | + |
| 12 | +**描述**: |
| 13 | + |
| 14 | +给定一个整数数组 $arr$,以及一个整数 $target$ 作为目标值。 |
| 15 | + |
| 16 | +**要求**: |
| 17 | + |
| 18 | +返回满足 $i < j < k$ 且 $arr[i] + arr[j] + arr[k] == target$ 的元组 $i, j, k$ 的数量。 |
| 19 | + |
| 20 | +由于结果会非常大,请返回 $10^9 + 7$ 的模。 |
| 21 | + |
| 22 | +**说明**: |
| 23 | + |
| 24 | +- $3 \le arr.length \le 3000$。 |
| 25 | +- $0 \le arr[i] \le 10^{3}$。 |
| 26 | +- $0 \le target \le 300$。 |
| 27 | + |
| 28 | +**示例**: |
| 29 | + |
| 30 | +- 示例 1: |
| 31 | + |
| 32 | +```python |
| 33 | +输入:arr = [1,1,2,2,3,3,4,4,5,5], target = 8 |
| 34 | +输出:20 |
| 35 | +解释: |
| 36 | +按值枚举(arr[i], arr[j], arr[k]): |
| 37 | +(1, 2, 5) 出现 8 次; |
| 38 | +(1, 3, 4) 出现 8 次; |
| 39 | +(2, 2, 4) 出现 2 次; |
| 40 | +(2, 3, 3) 出现 2 次。 |
| 41 | +``` |
| 42 | + |
| 43 | +- 示例 2: |
| 44 | + |
| 45 | +```python |
| 46 | +输入:arr = [1,1,2,2,2,2], target = 5 |
| 47 | +输出:12 |
| 48 | +解释: |
| 49 | +arr[i] = 1, arr[j] = arr[k] = 2 出现 12 次: |
| 50 | +我们从 [1,1] 中选择一个 1,有 2 种情况, |
| 51 | +从 [2,2,2,2] 中选出两个 2,有 6 种情况。 |
| 52 | +``` |
| 53 | + |
| 54 | +## 解题思路 |
| 55 | + |
| 56 | +### 思路 1:哈希表 + 双指针 |
| 57 | + |
| 58 | +#### 思路 |
| 59 | + |
| 60 | +这道题要求统计满足 $i < j < k$ 且 $arr[i] + arr[j] + arr[k] = target$ 的三元组数量。 |
| 61 | + |
| 62 | +由于数组元素范围较小($0 \le arr[i] \le 100$),我们可以使用哈希表统计每个数字的出现次数,然后枚举所有可能的三元组: |
| 63 | + |
| 64 | +1. **统计频次**:使用哈希表 $count$ 统计每个数字的出现次数。 |
| 65 | +2. **枚举三元组**:枚举所有可能的 $(i, j, k)$ 组合,其中 $i \le j \le k$: |
| 66 | + - 如果 $i + j + k = target$,计算该组合的方案数。 |
| 67 | + - 方案数的计算需要考虑三个数是否相同: |
| 68 | + - 如果三个数都相同:$C(count[i], 3) = \frac{count[i] \times (count[i] - 1) \times (count[i] - 2)}{6}$ |
| 69 | + - 如果两个数相同:$C(count[i], 2) \times count[k] = \frac{count[i] \times (count[i] - 1)}{2} \times count[k]$ |
| 70 | + - 如果三个数都不同:$count[i] \times count[j] \times count[k]$ |
| 71 | +3. 返回总方案数对 $10^9 + 7$ 取模的结果。 |
| 72 | + |
| 73 | +#### 代码 |
| 74 | + |
| 75 | +```python |
| 76 | +class Solution: |
| 77 | + def threeSumMulti(self, arr: List[int], target: int) -> int: |
| 78 | + MOD = 10**9 + 7 |
| 79 | + from collections import Counter |
| 80 | + |
| 81 | + # 统计每个数字的出现次数 |
| 82 | + count = Counter(arr) |
| 83 | + res = 0 |
| 84 | + |
| 85 | + # 枚举所有可能的三元组 (i, j, k),其中 i <= j <= k |
| 86 | + for i in range(101): |
| 87 | + for j in range(i, 101): |
| 88 | + k = target - i - j |
| 89 | + if k < 0 or k > 100: |
| 90 | + continue |
| 91 | + |
| 92 | + if i == j == k: |
| 93 | + # 三个数都相同:C(count[i], 3) |
| 94 | + res += count[i] * (count[i] - 1) * (count[i] - 2) // 6 |
| 95 | + elif i == j: |
| 96 | + # 前两个数相同:C(count[i], 2) * count[k] |
| 97 | + if k > j: |
| 98 | + res += count[i] * (count[i] - 1) // 2 * count[k] |
| 99 | + elif j == k: |
| 100 | + # 后两个数相同:count[i] * C(count[j], 2) |
| 101 | + if k > i: |
| 102 | + res += count[i] * count[j] * (count[j] - 1) // 2 |
| 103 | + else: |
| 104 | + # 三个数都不同 |
| 105 | + if k > j: |
| 106 | + res += count[i] * count[j] * count[k] |
| 107 | + |
| 108 | + res %= MOD |
| 109 | + |
| 110 | + return res |
| 111 | +``` |
| 112 | + |
| 113 | +#### 复杂度分析 |
| 114 | + |
| 115 | +- **时间复杂度**:$O(n + C^2)$,其中 $n$ 是数组长度,$C = 101$ 是数字的范围。统计频次需要 $O(n)$,枚举三元组需要 $O(C^2)$。 |
| 116 | +- **空间复杂度**:$O(C)$,需要哈希表存储每个数字的频次。 |
0 commit comments