给定一个整数数组 A,以及一个整数 target 作为目标值,返回满足 i < j < k 且 A[i] + A[j] + A[k] == target 的元组 i, j, k 的数量。
由于结果会非常大,请返回 结果除以 10^9 + 7 的余数。
示例 1:
输入:A = [1,1,2,2,3,3,4,4,5,5], target = 8
输出:20
解释:
按值枚举(A[i],A[j],A[k]):
(1, 2, 5) 出现 8 次;
(1, 3, 4) 出现 8 次;
(2, 2, 4) 出现 2 次;
(2, 3, 3) 出现 2 次。
示例 2:
输入:A = [1,1,2,2,2,2], target = 5
输出:12
解释:
A[i] = 1,A[j] = A[k] = 2 出现 12 次:
我们从 [1,1] 中选择一个 1,有 2 种情况,
从 [2,2,2,2] 中选出两个 2,有 6 种情况。
提示:
3 <= A.length <= 3000
0 <= A[i] <= 100
0 <= target <= 300
思 路 分 析 : \color{blue}思路分析: 思路分析:这种类似排列组合的问题肯定会有很多道友直接上蛮力法,三层循环,时间复杂度在O(n3)级别,然而这种题一般测试数据量都比较大,所以还是尽量避开用蛮力法。
前面有一系列的“两数之和”、“三数之和”等等这种题目 LeetCode 两数之和系列,这道题也是类似的处理方法。
为了降低复杂度,我们首先对数组进行排序,排序之后我们使用thirdPtr指针作为三个数中最大的数,然后使用firstPtr、secondPtr指针在thirdPtr的左边扫描剩下的两个数,并且保持firstPtr < secondPtr,这样满足firstPtr < secondPtr < thirdPtr,防止重复计算。
class Solution {
public:
int threeSumMulti(vector<int>& A, int target) {
int resCount = 0, aSize = A.size();
sort(A.begin(), A.end());//升序排序
//hashMap[num]记录num在A数组中出现的次数,firstIndex[num]记录num在A(排序后)中第一次出现的下标,lastIndex[num]记录num在A(排序后)中最后一次次出现的下标
vector<int> hashMap(101, 0), firstIndex(101, 0), lastIndex(101, 0);
for (int i = 0; i < aSize; ++i){
hashMap[A[i]] += 1;//次数自增
lastIndex[A[i]] = i;//更新A[i]最后一次出现的下标
}
for (int i = aSize - 1; i >= 0; --i){
firstIndex[A[i]] = i;//更新A[i]第一次出现的下标
}
//thirdPtr从后往前扫描,固定三个数的右边界
for (int thirdPtr = aSize - 1; thirdPtr > 0; --thirdPtr){
hashMap[A[thirdPtr]] -= 1;//当secondPtr指向的值与thirdPtr指向的值相同时,这时修正A[thirdPtr]的次数
int firstPtr = 0, secondPtr = thirdPtr - 1, tempCount = 0, tempTarget = target - A[thirdPtr];//tempCount记录以thirdPtr为左边界的情况数
while (firstPtr < secondPtr){//firstPtr、secondPtr双指针
if (A[firstPtr] + A[secondPtr] < tempTarget){
firstPtr = lastIndex[A[firstPtr]] + 1;//右移firstPtr,增大A[firstPtr] + A[secondPtr]的和
}
else if (A[firstPtr] + A[secondPtr] == tempTarget){
if (A[firstPtr] == A[secondPtr]){//当A[firstPtr] 、A[secondPtr]相等,这时需要特别注意重复计算,保持firstPtr小于secondPtr,根据排列组合计算公式
tempCount += hashMap[A[firstPtr]] * (hashMap[A[firstPtr]] - 1) / 2;
break;
}
else {//当A[firstPtr]、A[secondPtr]不相等,组合情况为两个数次数乘积
tempCount += hashMap[A[firstPtr]] * hashMap[A[secondPtr]];
firstPtr = lastIndex[A[firstPtr]] + 1;//更新firstPtr
secondPtr = firstIndex[A[secondPtr]] - 1;//更新secondPtr
}
}
else {//左移secondPtr,减小A[firstPtr] + A[secondPtr]的和
secondPtr = firstIndex[A[secondPtr]] - 1;
}
}
resCount = (resCount + tempCount) % 1000000007;
}
return resCount;
}
};