开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第31天,点击查看活动详情
一、题目描述:
给定一个整数数组 arr ,以及一个整数 target 作为目标值,返回满足 i < j < k 且 arr[i] + arr[j] + arr[k] == target 的元组 i, j, k 的数量。
由于结果会非常大,请返回 10^9 + 7 的模。
示例 1:
输入:arr = [1,1,2,2,3,3,4,4,5,5], target = 8
输出:20
解释:
按值枚举(arr[i], arr[j], arr[k]):
(1, 2, 5) 出现 8 次;
(1, 3, 4) 出现 8 次;
(2, 2, 4) 出现 2 次;
(2, 3, 3) 出现 2 次。
示例 2:
输入:arr = [1,1,2,2,2,2], target = 5
输出:12
解释:
arr[i] = 1, arr[j] = arr[k] = 2 出现 12 次:
我们从 [1,1] 中选择一个 1,有 2 种情况,
从 [2,2,2,2] 中选出两个 2,有 6 种情况。
提示:
- 3 <= arr.length <= 3000
- 0 <= arr[i] <= 100
- 0 <= target <= 300
二、思路分析:
巧妙利用双指针,达到固定一个数,寻找剩下两个数。 特殊情况需要注意: 当找到一组组合时,需要判断两数是否相等。 若不相等,看看两数周围有没有和两数一样的数,统计它们,然后相乘。 由于我们先行排序了数组,若两数相等,说明中间已经没有其他不同于两数的数字存在了,我们需要统计一列相同数字的排列组合,然后将之加上就好了。 思路来自于官方~
三、AC 代码:
class Solution {
public:
int threeSumMulti(vector<int>& A, int target) {
//升序排序数组
sort(A.begin(),A.end());
long res = 0;
for(int i = 0;i<A.size();i++){
//确定双指针的目标组合数
int T = target-A[i];
int j = i+1;
int k = A.size()-1;
while(j<k){
if(A[j]+A[k]<T){
j++;
}else if(A[j]+A[k]>T){
k--;
}else{
//两数加起来的等于目标数T的情况有三种
//第一、二种合并处理
if(A[j]!=A[k]){
int left = 1;
int right = 1;
while(A[j]==A[j+1]){
left++;
j++;
}
while(A[k]==A[k-1]){
right++;
k--;
}
res += left*right;
j++;
k--;
}else{
//第三种
res+= (k-j)*(k-j+1)/2;
break;
}
}
}
}
res %= 1000000007;
return res;
}
};