1521. Find a Value of a Mysterious Function Closest to Target
Problem Description
You are given a mysterious function func
that takes an array and two indices l
and r
as parameters. The function returns the bitwise AND of all elements from index l
to index r
(inclusive) in the array. In other words, func(arr, l, r) = arr[l] & arr[l+1] & ... & arr[r]
.
Given an integer array arr
and an integer target
, you need to find indices l
and r
such that the absolute difference |func(arr, l, r) - target|
is minimized.
Return the minimum possible value of |func(arr, l, r) - target|
.
Constraints:
0 <= l, r < arr.length
- Both
l
andr
must be valid indices in the array
Example:
If you have an array and a target value, you need to find a subarray (defined by indices l
and r
) where the bitwise AND of all elements in that subarray is as close as possible to the target value. The answer is the minimum absolute difference you can achieve.
The key insight is that for any subarray [l, r]
, the function computes the cumulative bitwise AND operation on all elements within that range, and you want this result to be as close to the target value as possible.
Intuition
The key observation is that the bitwise AND operation has a monotonic property: when we extend a subarray by including more elements, the result either stays the same or decreases (it never increases). This is because AND operation can only turn bits from 1 to 0, never from 0 to 1.
If we fix the right endpoint r
and vary the left endpoint l
from r
down to 0
, the values of func(arr, l, r)
form a decreasing sequence. Moreover, since each element in the array is at most 10^6
(which requires about 20 bits to represent), there can be at most 20 distinct values as we vary l
. This is because each bit position can only change from 1 to 0 once as we extend the range leftward.
This suggests we can efficiently track all possible AND values ending at each position. For each position r
, instead of computing func(arr, l, r)
for all possible l
values (which would be O(n²)), we can maintain a set of all distinct AND values that can be obtained with subarrays ending at position r
.
When we move from position r
to r+1
, we can update our set of possible values by:
- Taking each value in the current set and performing AND with
arr[r+1]
(extending previous subarrays) - Adding
arr[r+1]
itself (starting a new subarray at positionr+1
)
Since the set contains at most 20 values at any point (due to the bit limit), this update process is very efficient. For each position, we check all values in our set against the target to find the minimum absolute difference.
This approach cleverly exploits the properties of bitwise AND and the bounded nature of the values to reduce what could be an O(n²) problem to O(n × log M) where M is the maximum value in the array.
Learn more about Segment Tree and Binary Search patterns.
Solution Approach
The implementation uses a hash table (set) to maintain all possible bitwise AND values ending at the current position.
Algorithm Steps:
-
Initialize: Start with the answer as
abs(arr[0] - target)
and create a sets
containing justarr[0]
. -
Iterate through the array: For each element
x
inarr
:- Update the set: Create a new set containing:
- All values from the previous set ANDed with
x
:{x & y for y in s}
- The current element itself:
{x}
- This is done with the expression:
s = {x & y for y in s} | {x}
- All values from the previous set ANDed with
- Update the set: Create a new set containing:
-
Update the minimum: For each value
y
in the updated sets
, calculateabs(y - target)
and update the answer with the minimum value found.
Implementation Details:
class Solution:
def closestToTarget(self, arr: List[int], target: int) -> int:
ans = abs(arr[0] - target) # Initialize with first element
s = {arr[0]} # Set to store all possible AND values ending at current position
for x in arr:
# Update set: AND all previous values with x, and add x itself
s = {x & y for y in s} | {x}
# Find minimum difference with target among all values in set
ans = min(ans, min(abs(y - target) for y in s))
return ans
Why this works:
- The set
s
at each iteration contains all possible values offunc(arr, l, i)
wherei
is the current index andl
ranges from0
toi
. - When we process element
arr[i]
, we extend all subarrays ending ati-1
by includingarr[i]
(hencex & y for y in s
). - We also start a new subarray at position
i
(hence adding{x}
to the set). - The set automatically handles duplicates, keeping only unique values.
- Since the set size is bounded by approximately 20 (the number of bits), each iteration is efficient.
Time Complexity: O(n × log M)
where n
is the array length and M
is the maximum value in the array. The log M
factor comes from the maximum number of distinct AND values possible.
Space Complexity: O(log M)
for storing the set of distinct AND values.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach.
Example: arr = [9, 12, 3, 7, 15]
, target = 5
We need to find a subarray where the bitwise AND of all elements is closest to 5.
Initial State:
ans = |9 - 5| = 4
s = {9}
(only considering subarray [9])
Iteration 1: Process arr[1] = 12
- Calculate new set:
s = {12 & 9} | {12} = {8} | {12} = {8, 12}
12 & 9 = 1100 & 1001 = 1000 = 8
(extending subarray [9] to [9,12])12
itself (new subarray starting at index 1)
- Update answer:
|8 - 5| = 3
|12 - 5| = 7
ans = min(4, 3) = 3
Iteration 2: Process arr[2] = 3
- Calculate new set:
s = {3 & 8, 3 & 12} | {3} = {0, 0} | {3} = {0, 3}
3 & 8 = 0011 & 1000 = 0000 = 0
(extending [9,12] to [9,12,3])3 & 12 = 0011 & 1100 = 0000 = 0
(extending [12] to [12,3])3
itself (new subarray [3])
- Update answer:
|0 - 5| = 5
|3 - 5| = 2
ans = min(3, 2) = 2
Iteration 3: Process arr[3] = 7
- Calculate new set:
s = {7 & 0, 7 & 3} | {7} = {0, 3} | {7} = {0, 3, 7}
7 & 0 = 0
(extending subarrays ending with AND value 0)7 & 3 = 0111 & 0011 = 0011 = 3
(extending [3] to [3,7])7
itself (new subarray [7])
- Update answer:
|0 - 5| = 5
|3 - 5| = 2
|7 - 5| = 2
ans = min(2, 2) = 2
Iteration 4: Process arr[4] = 15
- Calculate new set:
s = {15 & 0, 15 & 3, 15 & 7} | {15} = {0, 3, 7} | {15} = {0, 3, 7, 15}
15 & 0 = 0
15 & 3 = 1111 & 0011 = 0011 = 3
15 & 7 = 1111 & 0111 = 0111 = 7
(extending [7] to [7,15])15
itself
- Update answer:
|0 - 5| = 5
|3 - 5| = 2
|7 - 5| = 2
|15 - 5| = 10
ans = min(2, 2) = 2
Final Answer: 2
The closest we can get to target 5 is either:
- Subarray [3] with AND value = 3 (difference = 2)
- Subarray [7] with AND value = 7 (difference = 2)
- Subarray [3,7] with AND value = 3 (difference = 2)
This example demonstrates how the set maintains all possible AND values ending at each position, efficiently tracking only distinct values (notice how duplicate 0s are automatically handled), and how we continuously update our minimum difference.
Solution Implementation
1class Solution:
2 def closestToTarget(self, arr: List[int], target: int) -> int:
3 # Initialize the minimum difference with the first element
4 min_difference = abs(arr[0] - target)
5
6 # Set to store all possible AND values ending at current position
7 current_and_values = {arr[0]}
8
9 # Iterate through each element in the array
10 for num in arr:
11 # Create new set of AND values by:
12 # 1. AND-ing current number with all previous AND values
13 # 2. Including the current number itself as a new subsequence start
14 current_and_values = {num & prev_value for prev_value in current_and_values} | {num}
15
16 # Update minimum difference by checking all current AND values
17 min_difference = min(min_difference, min(abs(and_value - target) for and_value in current_and_values))
18
19 return min_difference
20
1class Solution {
2 public int closestToTarget(int[] arr, int target) {
3 // Initialize the minimum difference with the first element
4 int minDifference = Math.abs(arr[0] - target);
5
6 // Set to store all possible AND values ending at the previous position
7 Set<Integer> previousAndValues = new HashSet<>();
8 previousAndValues.add(arr[0]);
9
10 // Iterate through each element in the array
11 for (int currentElement : arr) {
12 // Set to store all possible AND values ending at current position
13 Set<Integer> currentAndValues = new HashSet<>();
14
15 // For each previous AND value, compute AND with current element
16 // This represents extending previous subarrays to include current element
17 for (int previousValue : previousAndValues) {
18 currentAndValues.add(currentElement & previousValue);
19 }
20
21 // Add the current element itself (subarray of length 1)
22 currentAndValues.add(currentElement);
23
24 // Update the minimum difference with all possible AND values at current position
25 for (int andValue : currentAndValues) {
26 minDifference = Math.min(minDifference, Math.abs(andValue - target));
27 }
28
29 // Move to the next position
30 previousAndValues = currentAndValues;
31 }
32
33 return minDifference;
34 }
35}
36
1class Solution {
2public:
3 int closestToTarget(vector<int>& arr, int target) {
4 // Initialize the minimum difference with the first element
5 int minDifference = abs(arr[0] - target);
6
7 // Set to store all possible AND values ending at the previous position
8 unordered_set<int> previousValues;
9 previousValues.insert(arr[0]);
10
11 // Iterate through each element in the array
12 for (int currentNum : arr) {
13 // Set to store all possible AND values ending at current position
14 unordered_set<int> currentValues;
15
16 // The current number itself forms a subarray of length 1
17 currentValues.insert(currentNum);
18
19 // For each previous AND value, compute AND with current number
20 // This extends all subarrays ending at previous position to current position
21 for (int previousValue : previousValues) {
22 currentValues.insert(currentNum & previousValue);
23 }
24
25 // Update the minimum difference with all possible AND values at current position
26 for (int andValue : currentValues) {
27 minDifference = min(minDifference, abs(andValue - target));
28 }
29
30 // Move current values to previous for next iteration
31 previousValues = move(currentValues);
32 }
33
34 return minDifference;
35 }
36};
37
1/**
2 * Finds the minimum absolute difference between target and any possible bitwise AND
3 * of a contiguous subarray
4 * @param arr - The input array of integers
5 * @param target - The target value to get closest to
6 * @returns The minimum absolute difference
7 */
8function closestToTarget(arr: number[], target: number): number {
9 // Initialize answer with the difference between first element and target
10 let minDifference: number = Math.abs(arr[0] - target);
11
12 // Set to store all possible AND values ending at previous position
13 let previousAndValues: Set<number> = new Set<number>();
14 previousAndValues.add(arr[0]);
15
16 // Iterate through each element in the array
17 for (const currentElement of arr) {
18 // Set to store all possible AND values ending at current position
19 const currentAndValues: Set<number> = new Set<number>();
20
21 // Add current element itself (subarray of length 1)
22 currentAndValues.add(currentElement);
23
24 // Calculate AND with all previous values to extend subarrays
25 for (const previousValue of previousAndValues) {
26 currentAndValues.add(currentElement & previousValue);
27 }
28
29 // Update minimum difference with all possible AND values at current position
30 for (const andValue of currentAndValues) {
31 minDifference = Math.min(minDifference, Math.abs(andValue - target));
32 }
33
34 // Move current values to previous for next iteration
35 previousAndValues = currentAndValues;
36 }
37
38 return minDifference;
39}
40
Time and Space Complexity
Time Complexity: O(n * log(max(arr)))
The algorithm iterates through each element in the array once. For each element x
, it performs bitwise AND operations with all elements in set s
. The key insight is that the set s
contains at most O(log(max(arr)))
distinct values at any point.
This is because when we perform bitwise AND operations starting from any number, the result can only decrease or stay the same (never increase). Since bitwise AND can only turn bits from 1 to 0 (never from 0 to 1), and each number has at most log(max(arr))
bits, there can be at most O(log(max(arr)))
distinct values in the set after consecutive AND operations.
Therefore:
- Outer loop:
O(n)
iterations - Inner set comprehension:
O(log(max(arr)))
operations per iteration - Total:
O(n * log(max(arr)))
Space Complexity: O(log(max(arr)))
The space is primarily used by the set s
. As explained above, the set s
can contain at most O(log(max(arr)))
distinct values at any point during the execution, since we're tracking the results of cumulative bitwise AND operations which can only produce a limited number of distinct values based on the bit representation of the numbers.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Forgetting to Initialize with the First Element
Issue: A common mistake is to start with an empty set or not properly initialize the answer with the first element's difference from the target.
Wrong Implementation:
def closestToTarget(self, arr: List[int], target: int) -> int:
min_difference = float('inf') # Wrong: might miss single-element subarrays
current_and_values = set() # Wrong: empty set loses first element
for num in arr:
current_and_values = {num & prev_value for prev_value in current_and_values} | {num}
min_difference = min(min_difference, min(abs(and_value - target) for and_value in current_and_values))
return min_difference
Correct Approach:
def closestToTarget(self, arr: List[int], target: int) -> int:
min_difference = abs(arr[0] - target) # Initialize with first element
current_and_values = {arr[0]} # Start with first element in set
for num in arr[1:]: # Process remaining elements
current_and_values = {num & prev_value for prev_value in current_and_values} | {num}
min_difference = min(min_difference, min(abs(and_value - target) for and_value in current_and_values))
return min_difference
Pitfall 2: Not Including Single Elements as Valid Subarrays
Issue: Forgetting to add {num}
when updating the set, which would miss all single-element subarrays starting at the current position.
Wrong Implementation:
def closestToTarget(self, arr: List[int], target: int) -> int:
min_difference = abs(arr[0] - target)
current_and_values = {arr[0]}
for num in arr:
# Wrong: only extends previous subarrays, doesn't start new ones
current_and_values = {num & prev_value for prev_value in current_and_values}
min_difference = min(min_difference, min(abs(and_value - target) for and_value in current_and_values))
return min_difference
Correct Approach:
def closestToTarget(self, arr: List[int], target: int) -> int:
min_difference = abs(arr[0] - target)
current_and_values = {arr[0]}
for num in arr:
# Correct: includes both extended subarrays AND new subarray starting at current position
current_and_values = {num & prev_value for prev_value in current_and_values} | {num}
min_difference = min(min_difference, min(abs(and_value - target) for and_value in current_and_values))
return min_difference
Pitfall 3: Processing the First Element Twice
Issue: If you initialize the set with arr[0]
and then iterate through the entire array starting from index 0, you'll process the first element twice, which could lead to incorrect results.
Wrong Implementation:
def closestToTarget(self, arr: List[int], target: int) -> int:
min_difference = abs(arr[0] - target)
current_and_values = {arr[0]}
# Wrong: starts from index 0, processing arr[0] again
for num in arr:
current_and_values = {num & prev_value for prev_value in current_and_values} | {num}
min_difference = min(min_difference, min(abs(and_value - target) for and_value in current_and_values))
return min_difference
Correct Approaches:
Option 1: Start iteration from index 1
def closestToTarget(self, arr: List[int], target: int) -> int:
min_difference = abs(arr[0] - target)
current_and_values = {arr[0]}
for num in arr[1:]: # Start from index 1
current_and_values = {num & prev_value for prev_value in current_and_values} | {num}
min_difference = min(min_difference, min(abs(and_value - target) for and_value in current_and_values))
return min_difference
Option 2: Don't initialize, process everything in loop
def closestToTarget(self, arr: List[int], target: int) -> int:
min_difference = float('inf')
current_and_values = set()
for num in arr:
current_and_values = {num & prev_value for prev_value in current_and_values} | {num}
min_difference = min(min_difference, min(abs(and_value - target) for and_value in current_and_values))
return min_difference
Note: While the original solution processes the first element twice, it still works correctly because arr[0] & arr[0] = arr[0]
, making it idempotent. However, it's cleaner to avoid the redundancy.
Which of these pictures shows the visit order of a depth-first search?
Recommended Readings
Segment Tree Faster Range Queries For this article we want to introduce the idea of a Segment Tree Segment Trees allow us to quickly perform range queries as well as range updates Suppose we have an array and we want to know the sum of a particular range of numbers as well
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!