leetcode 923. 3Sum With Multiplicity(含重复数字的3Sum)

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.

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

在数组arr中找到3个数,使它们的和==target,问有多少组这样的3个数。

思路:
1.HashMap
遍历arr,下标为i,我们要找到target - arr[i],也就是0~i-1范围剩下两个数的和出现了多少次,
然后统计 0~ i-1 下标对应所有数字与arr[ i ]的和出现的次数。这个就是到 i 为止所有两个数的和。
那么到 i+1 时,0~i 范围内所有两个数字和的次数都统计完了,再找target - arr[i+1]出现的次数,加到结果里面。
直到遍历结束。

注意用int型加着加着,在取mod前就会溢出,所以要用long型。

但是这个方法比较慢。

    public int threeSumMulti(int[] arr, int target) {
        int n = arr.length;
        HashMap<Integer, Long> map = new HashMap<>();
        long result = 0;
        int mod = 1000000007;
        
        for(int i = 0; i < n; i ++) {
            result += map.getOrDefault(target - arr[i], 0l);
            
            for(int j = 0; j < i; j++) {
                int sum = arr[i] + arr[j];
                map.put(sum, map.getOrDefault(sum, 0l) + 1l);
            }
        }
        return (int)(result % mod);
    }

2.利用0 <= arr[i] <= 100,
不管数组arr有多长,我就遍历0~100这些数字即可,反正3个元素跑不出这个范围。

但是为了避免出现重复的情况,比如[1, 2, 3]和[2,1,3]和[3,1,2]是同一组3个数字。这中间只取一种来计算,默认取升序排列的。
设3个数字为i, j, k, 取 i <= j <= k这种情况。

这时又分3种情况
1)i == j == k
比如[2, 2, 2], target = 6
2出现了3次,所以次数=3x2x1?然而其实只有一种,所以这种情况要➗6
2)i == j < k
[2, 2, 3], 次数=(2x1)x1?其实仍然是1种,这种情况要➗2
3)i < j < k
用i, j, k三个数字出现的次数相乘即可。

仍然要注意三个数字的次数相乘,在取mod之前就可能溢出,所以要取long型。

    public int threeSumMulti(int[] arr, int target) {        
        long[] cnt = new long[101];
        long result = 0;
        int mod = 1000000007;
        
        for(int tmp : arr) cnt[tmp] ++;
        
        for(int i = 0; i <= 100; i++) {
            for(int j = i; j <= 100; j++) {
                int k = target - i - j;
                if(k < 0 || k > 100) continue;
                if(cnt[i] == 0 || cnt[j] == 0 || cnt[k] == 0) continue;
                
                if(i == j && j == k) {
                    result += cnt[i] * (cnt[i]-1) * (cnt[i]-2) / 6 ;
                } else if(i == j && j != k) {
                    result += cnt[i] * (cnt[i]-1) / 2 * cnt[k];
                } else if(j < k) {
                    result += cnt[i] * cnt[j] * cnt[k];
                }
                result %= mod;
                
            }
        }
        return (int)result % mod;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝羽飞鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值