[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 }

 

two sum题目总结

LeetCode 题目总结

posted @ 2021-02-05 03:12  CNoodle  阅读(146)  评论(0编辑  收藏  举报