{x}
blog image

Closest Subsequence Sum

You are given an integer array nums and an integer goal.

You want to choose a subsequence of nums such that the sum of its elements is the closest possible to goal. That is, if the sum of the subsequence's elements is sum, then you want to minimize the absolute difference abs(sum - goal).

Return the minimum possible value of abs(sum - goal).

Note that a subsequence of an array is an array formed by removing some elements (possibly all or none) of the original array.

 

Example 1:

Input: nums = [5,-7,3,5], goal = 6
Output: 0
Explanation: Choose the whole array as a subsequence, with a sum of 6.
This is equal to the goal, so the absolute difference is 0.

Example 2:

Input: nums = [7,-9,15,-2], goal = -5
Output: 1
Explanation: Choose the subsequence [7,-9,-2], with a sum of -4.
The absolute difference is abs(-4 - (-5)) = abs(1) = 1, which is the minimum.

Example 3:

Input: nums = [1,2,3], goal = -7
Output: 7

 

Constraints:

  • 1 <= nums.length <= 40
  • -107 <= nums[i] <= 107
  • -109 <= goal <= 109

Solution Explanation

This problem asks to find the minimum absolute difference between the sum of a subsequence of the input array nums and a given goal. The constraints suggest that a brute-force approach (iterating through all possible subsequences) would be too slow. The efficient solutions leverage a divide-and-conquer strategy combined with binary search.

Core Idea:

  1. Divide: Split the input array nums into two halves.
  2. Conquer: Generate all possible subsequence sums for each half recursively. This is done using a Depth-First Search (DFS) function. Each subsequence sum is stored in a set (or list in Python).
  3. Combine: For each sum (l) from the left half's set, find the closest sum (r) in the sorted right half's set such that l + r is closest to the goal. Binary search is used to efficiently find this closest sum.
  4. Result: The minimum absolute difference is the minimum difference found across all pairs of sums from the left and right halves.

Time Complexity Analysis:

  • DFS: The time complexity of the DFS function is O(2n/2), where n is the length of nums. This is because each element can either be included or excluded in a subsequence, resulting in 2n/2 possibilities for each half.
  • Binary Search: The binary search within the sorted right half takes O(log(2n/2)) = O(n/2) time in the worst case.
  • Combining: The combination step iterates through the left half's set (size approximately 2n/2) and performs a binary search for each element. Therefore, the time complexity of this step is O(2n/2 * n/2).

Overall Time Complexity: The overall time complexity is dominated by the DFS and combining steps, which are both approximately O(n * 2n/2). This is significantly better than the brute-force O(n * 2n) approach.

Space Complexity Analysis:

The space complexity is dominated by the sets (or lists) used to store the subsequence sums. Each set can contain approximately 2n/2 elements. Therefore, the space complexity is O(2n/2).

Code Explanation (Python):

The Python code efficiently implements this strategy:

class Solution:
    def minAbsDifference(self, nums: List[int], goal: int) -> int:
        n = len(nums)
        left = set()
        right = set()
 
        self.getSubSeqSum(0, 0, nums[: n // 2], left)
        self.getSubSeqSum(0, 0, nums[n // 2 :], right)
 
        result = inf
        right = sorted(right)
        rl = len(right)
 
        for l in left:
            remaining = goal - l
            idx = bisect_left(right, remaining)
 
            if idx < rl:
                result = min(result, abs(remaining - right[idx]))
 
            if idx > 0:
                result = min(result, abs(remaining - right[idx - 1]))
 
        return result
 
    def getSubSeqSum(self, i: int, curr: int, arr: List[int], result: Set[int]):
        if i == len(arr):
            result.add(curr)
            return
 
        self.getSubSeqSum(i + 1, curr, arr, result)
        self.getSubSeqSum(i + 1, curr + arr[i], arr, result)
 

The getSubSeqSum function recursively generates all subsequence sums for a given array portion. The main function then combines the results from both halves using binary search to find the closest sum to the goal. bisect_left from the bisect module efficiently finds the insertion point for a value in a sorted list.

The other languages (Java, C++, Go) implement the same algorithm with minor syntactic differences. They use similar data structures and techniques to achieve the same time and space complexity.