Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1521. Find a Value of a Mysterious Function Closest to Target - Leetcode Solution

Problem Description

Given an integer array arr and an integer target, you are to find the minimum absolute difference between target and the value of a mysterious function f applied to any subarray of arr.
The mysterious function f for a subarray is defined as the bitwise AND of all its elements.
Formally, for a subarray arr[i..j], f(arr[i..j]) = arr[i] & arr[i+1] & ... & arr[j].
You must return the minimum value of |f(arr[i..j]) - target| for any subarray arr[i..j].

Constraints:

  • 1 ≤ arr.length ≤ 105
  • 1 ≤ arr[i] ≤ 106
  • 0 ≤ target ≤ 106
Key points:
  • Each subarray must be contiguous.
  • You can use any subarray (including length 1).
  • There is exactly one valid answer.
  • Do not reuse elements outside the selected subarray.

Thought Process

At first glance, it seems you might need to check every possible subarray, compute the bitwise AND, and compare to the target. But with up to 100,000 elements, trying all subarrays would be far too slow.
Let's consider how the bitwise AND works: for any two numbers, the AND operation can only keep or clear bits, never set new ones. As you AND more numbers, the result can only decrease or stay the same.
This means that as you extend a subarray, its AND value can never increase. This property is key for optimization.
We need a way to efficiently consider all possible subarrays’ ANDs without redundant recalculation.

Solution Approach

Let's break down the optimized solution step by step:

  1. Iterate through the array: For each position in arr, consider all subarrays ending at that position.
  2. Track unique ANDs: For each new element, keep a set of all possible AND values for subarrays ending at this position. This prevents redundant calculations since extending a subarray can only reduce the AND value.
  3. Efficiently update ANDs: For each previous AND value, AND it with the current element to get new possible ANDs for this position. Also, include the current element itself (the subarray of length 1).
  4. Update the answer: For each new AND value, compute abs(AND - target) and keep track of the minimum found so far.
  5. Why does this work? The number of unique AND values per position is small (logarithmic), because bits can only be cleared, not set, as you AND more numbers. This keeps the algorithm efficient.

We use a set to store unique AND values for each subarray ending at the current index. This ensures we do not process duplicate AND values, optimizing our approach.

Example Walkthrough

Let's consider arr = [9,12,3,7,15] and target = 5.

  • Step 1: Start with 9.
    - Subarray: [9], AND: 9, |9-5| = 4
  • Step 2: Add 12.
    - [12]: 12, |12-5|=7
    - [9,12]: 9&12=8, |8-5|=3
  • Step 3: Add 3.
    - [3]: 3, |3-5|=2
    - [12,3]: 12&3=0, |0-5|=5
    - [9,12,3]: 8&3=0, |0-5|=5
  • Step 4: Add 7.
    - [7]: 7, |7-5|=2
    - [3,7]: 3&7=3, |3-5|=2
    - [12,3,7]: 0&7=0, |0-5|=5
    - [9,12,3,7]: 0&7=0, |0-5|=5
  • Step 5: Add 15.
    - [15]: 15, |15-5|=10
    - [7,15]: 7&15=7, |7-5|=2
    - [3,7,15]: 3&7&15=3, |3-5|=2
    - [12,3,7,15]: 0&15=0, |0-5|=5
    - [9,12,3,7,15]: 0&15=0, |0-5|=5

The minimum absolute difference found is 2 (for AND values 3 and 7).

Code Implementation

class Solution:
    def closestToTarget(self, arr, target):
        ans = float('inf')
        prev = set()
        for num in arr:
            curr = {num}
            for v in prev:
                curr.add(v & num)
            for v in curr:
                ans = min(ans, abs(v - target))
            prev = curr
        return ans
      
class Solution {
public:
    int closestToTarget(vector<int>& arr, int target) {
        unordered_set<int> prev;
        int ans = INT_MAX;
        for (int num : arr) {
            unordered_set<int> curr;
            curr.insert(num);
            for (int v : prev) {
                curr.insert(v & num);
            }
            for (int v : curr) {
                ans = min(ans, abs(v - target));
            }
            prev = curr;
        }
        return ans;
    }
};
      
import java.util.*;

class Solution {
    public int closestToTarget(int[] arr, int target) {
        Set<Integer> prev = new HashSet<>();
        int ans = Integer.MAX_VALUE;
        for (int num : arr) {
            Set<Integer> curr = new HashSet<>();
            curr.add(num);
            for (int v : prev) {
                curr.add(v & num);
            }
            for (int v : curr) {
                ans = Math.min(ans, Math.abs(v - target));
            }
            prev = curr;
        }
        return ans;
    }
}
      
var closestToTarget = function(arr, target) {
    let ans = Infinity;
    let prev = new Set();
    for (let num of arr) {
        let curr = new Set([num]);
        for (let v of prev) {
            curr.add(v & num);
        }
        for (let v of curr) {
            ans = Math.min(ans, Math.abs(v - target));
        }
        prev = curr;
    }
    return ans;
};
      

Time and Space Complexity

Brute-force:
- Checking all subarrays: O(n2) time, since there are O(n2) subarrays.
- Each AND operation takes O(1), but the number of subarrays is prohibitive for large n.

Optimized approach:
- For each index, we keep a set of unique AND values. Each number can only clear bits, so the number of unique ANDs per position is O(log(max(arr[i]))), which is about 20 for 106.
- Total time: O(n * log(max(arr[i])))
- Space: O(log(max(arr[i]))) for the set at each iteration.

Why? The set does not grow unbounded because repeated ANDs quickly reduce the number of unique values.

Summary

We leveraged the properties of the bitwise AND operation to avoid brute-force enumeration of all subarrays. By keeping track of all unique AND values for subarrays ending at each position, we efficiently found the minimum absolute difference to the target. The solution is elegant because it converts a seemingly quadratic problem into a near-linear one, thanks to the mathematical constraints of the AND operation.