classSolution { publicintthreeSumMulti(int[] A, int target) { intMOD=1_000_000_007; longans=0; Arrays.sort(A);
for (inti=0; i < A.length; ++i) { // We'll try to find the number of i < j < k // with A[j] + A[k] == T, where T = target - A[i].
// The below is a "two sum with multiplicity". intT= target - A[i]; intj= i+1, k = A.length - 1;
while (j < k) { // These steps proceed as in a typical two-sum. if (A[j] + A[k] < T) j++; elseif (A[j] + A[k] > T) k--; elseif (A[j] != A[k]) { // We have A[j] + A[k] == T. // Let's count "left": the number of A[j] == A[j+1] == A[j+2] == ... // And similarly for "right". intleft=1, right = 1; while (j+1 < k && A[j] == A[j+1]) { left++; j++; } while (k-1 > j && A[k] == A[k-1]) { right++; k--; }
ans += left * right; ans %= MOD; j++; k--; } else { // M = k - j + 1 // We contributed M * (M-1) / 2 pairs. ans += (k-j+1) * (k-j) / 2; ans %= MOD; break; } } }
classSolution(object): defthreeSumMulti(self, A, target): MOD = 10**9 + 7 ans = 0 A.sort()
for i, x inenumerate(A): # We'll try to find the number of i < j < k # with A[j] + A[k] == T, where T = target - A[i].
# The below is a "two sum with multiplicity". T = target - A[i] j, k = i+1, len(A) - 1
while j < k: # These steps proceed as in a typical two-sum. if A[j] + A[k] < T: j += 1 elif A[j] + A[k] > T: k -= 1 # These steps differ: elif A[j] != A[k]: # We have A[j] + A[k] == T. # Let's count "left": the number of A[j] == A[j+1] == A[j+2] == ... # And similarly for "right". left = right = 1 while j + 1 < k and A[j] == A[j+1]: left += 1 j += 1 while k - 1 > j and A[k] == A[k-1]: right += 1 k -= 1
# We contributed left * right many pairs. ans += left * right ans %= MOD j += 1 k -= 1
else: # M = k - j + 1 # We contributed M * (M-1) / 2 pairs. ans += (k-j+1) * (k-j) / 2 ans %= MOD break
return ans
复杂度分析
时间复杂度: O(N^2),其中 N 为 A 的长度。
空间复杂度: O(1)。
方法二: 数学法
思路和算法
设 count[x] 为数组 A 中 x 出现的次数。对于每种 x+y+z == target,可以数一下有多少种可能的组合,这里可以看几个例子:
classSolution { publicintthreeSumMulti(int[] A, int target) { intMOD=1_000_000_007; long[] count = newlong[101]; for (int x: A) count[x]++;
longans=0;
// All different for (intx=0; x <= 100; ++x) for (inty= x+1; y <= 100; ++y) { intz= target - x - y; if (y < z && z <= 100) { ans += count[x] * count[y] * count[z]; ans %= MOD; } }
// x == y != z for (intx=0; x <= 100; ++x) { intz= target - 2*x; if (x < z && z <= 100) { ans += count[x] * (count[x] - 1) / 2 * count[z]; ans %= MOD; } }
// x != y == z for (intx=0; x <= 100; ++x) { if (target % 2 == x % 2) { inty= (target - x) / 2; if (x < y && y <= 100) { ans += count[x] * count[y] * (count[y] - 1) / 2; ans %= MOD; } } }
// x == y == z if (target % 3 == 0) { intx= target / 3; if (0 <= x && x <= 100) { ans += count[x] * (count[x] - 1) * (count[x] - 2) / 6; ans %= MOD; } }
classSolution(object): defthreeSumMulti(self, A, target): MOD = 10**9 + 7 count = [0] * 101 for x in A: count[x] += 1
ans = 0
# All different for x in xrange(101): for y in xrange(x+1, 101): z = target - x - y if y < z <= 100: ans += count[x] * count[y] * count[z] ans %= MOD
# x == y for x in xrange(101): z = target - 2*x if x < z <= 100: ans += count[x] * (count[x] - 1) / 2 * count[z] ans %= MOD
# y == z for x in xrange(101): if (target - x) % 2 == 0: y = (target - x) / 2 if x < y <= 100: ans += count[x] * count[y] * (count[y] - 1) / 2 ans %= MOD
# x == y == z if target % 3 == 0: x = target / 3 if0 <= x <= 100: ans += count[x] * (count[x] - 1) * (count[x] - 2) / 6 ans %= MOD
return ans
复杂度分析
时间复杂度: O(N + W^2),其中 N 为 A 的长度,W 为 A[i] 中最大的数。
空间复杂度: O(W)。
方法三: 变种的三数之和
思路和算法那
在 方法二 中,count[x] 为 A 中 x 出现的次数。同时,让 keys 为数组 A 中所有元素只出现一次的有序数组。接着用三数之和的方法来处理 keys。
classSolution { publicintthreeSumMulti(int[] A, int target) { intMOD=1_000_000_007;
// Initializing as long saves us the trouble of // managing count[x] * count[y] * count[z] overflowing later. long[] count = newlong[101]; intuniq=0; for (int x: A) { count[x]++; if (count[x] == 1) uniq++; }
int[] keys = newint[uniq]; intt=0; for (inti=0; i <= 100; ++i) if (count[i] > 0) keys[t++] = i;
longans=0; // Now, let's do a 3sum on "keys", for i <= j <= k. // We will use count to add the correct contribution to ans.
for (inti=0; i < keys.length; ++i) { intx= keys[i]; intT= target - x; intj= i, k = keys.length - 1; while (j <= k) { inty= keys[j], z = keys[k]; if (y + z < T) { j++; } elseif (y + z > T) { k--; } else { // # x+y+z == T, now calc the size of the contribution if (i < j && j < k) { ans += count[x] * count[y] * count[z]; } elseif (i == j && j < k) { ans += count[x] * (count[x] - 1) / 2 * count[z]; } elseif (i < j && j == k) { ans += count[x] * count[y] * (count[y] - 1) / 2; } else { // i == j == k ans += count[x] * (count[x] - 1) * (count[x] - 2) / 6; }
classSolution(object): defthreeSumMulti(self, A, target): MOD = 10**9 + 7 count = collections.Counter(A) keys = sorted(count)
ans = 0
# Now, let's do a 3sum on "keys", for i <= j <= k. # We will use count to add the correct contribution to ans. for i, x inenumerate(keys): T = target - x j, k = i, len(keys) - 1 while j <= k: y, z = keys[j], keys[k] if y + z < T: j += 1 elif y + z > T: k -= 1 else: # x+y+z == T, now calculate the size of the contribution if i < j < k: ans += count[x] * count[y] * count[z] elif i == j < k: ans += count[x] * (count[x] - 1) / 2 * count[z] elif i < j == k: ans += count[x] * count[y] * (count[y] - 1) / 2 else: # i == j == k ans += count[x] * (count[x] - 1) * (count[x] - 2) / 6