923. 三数之和的多种可能

64 阅读1分钟

题目:
给定一个整数数组 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]
}