题目:
给定一个整数数组 arr ,以及一个整数 target 作为目标值,返回满足 i < j < k 且 arr[i] + arr[j] + arr[k] == target 的元组 i, j, k 的数量。
由于结果会非常大,请返回 109 + 7 的模。
算法:
方法一:暴力
TLE
func threeSumMulti(arr []int, target int) int {
sort.Ints(arr)
n := len(arr)
num := 0
mod := 1000000007
ans := 0
for i := 0; i <= n - 3; i ++ {
for j := i + 1; j <= n - 2; j ++ {
for k := j + 1; k <= n - 1; k ++ {
num = arr[i] + arr[j] + arr[k]
if num > target {
break
}
if num == target {
ans = (ans + 1) % mod
}
}
}
}
return ans
}
方法二:二数之和改进
func threeSumMulti(arr []int, target int) int {
sort.Ints(arr)
n := len(arr)
mod := 1000000007
ans := 0
for i := 0; i <= n - 3; i ++ {
j, k := i + 1, n - 1
T := target - arr[i]
// 双指针
for j < k {
sum := arr[j] + arr[k]
if sum > T {
k --
} else if sum < T {
j ++
// 分类讨论组合数量
} else if arr[j] != arr[k] {
// 找导符合条件的left, right ,arr[j, left] + arr[right, k ] == T
left, right := j, k
for left + 1 < right && arr[left] == arr[left + 1] {
left ++
}
for left < right - 1 && arr[right] == arr[right - 1] {
right --
}
leftCount , rightCount := left - j + 1, k - right + 1
ans = (ans + leftCount * rightCount) % mod
// fmt.Println(i, j, left, right, k, T, ans)
j = left + 1
k = right - 1
} else {
// $C_m^2$
m := k - j +1
ans = (ans + m * (m - 1) / 2) % mod
// fmt.Println("---", i, j, k, m, ans)
break
}
}
}
return ans
}
方法三:动态规划
时间、空间复杂度:
3000 x 3 x 300 = 2.7 x 10^6
func threeSumMulti(arr []int, target int) int {
n := len(arr)
mod := 1000000007
// dp[i][j][k] 从前i个数,取j个,和为k的组合数目
dp := make([][][]int, n + 1)
for i := range dp {
dp[i] = make([][]int, 4)
for j := range dp[i] {
dp[i][j] = make([]int, target + 1)
}
dp[i][0][0] = 1
}
for i := 1; i <= n; i ++ {
for j := 1; j <= 3; j ++ {
for k := 0; k <= target; k ++ {
if arr[i - 1] <= k {
// 选上当前这个数,符合条件的组合数dp[i][j][k]
dp[i][j][k] = dp[i - 1][j - 1][k - arr[i - 1]]
}
// 选择当前的数,或者不选则当前的数 得到j数之和为k的组合数
dp[i][j][k] = (dp[i][j][k] + dp[i - 1][j][k]) % mod
}
}
}
return dp[n][3][target]
}