Facebook Pixel

2935. Maximum Strong Pair XOR II

HardBit ManipulationTrieArrayHash TableSliding Window
Leetcode Link

Problem Description

You are given a 0-indexed integer array nums. Your task is to find the maximum XOR value among all strong pairs in the array.

A pair of integers (x, y) is called a strong pair if it satisfies the condition:

  • |x - y| <= min(x, y)

This means the absolute difference between x and y should not exceed the smaller value of the two numbers.

You need to:

  1. Select two integers from nums that form a strong pair
  2. Calculate their bitwise XOR
  3. Return the maximum XOR value possible among all valid strong pairs

Important Notes:

  • You can pick the same integer twice to form a pair (e.g., (nums[i], nums[i]) is valid)
  • The indices of the elements don't matter - you're looking at the values themselves

Example of strong pairs:

  • (2, 3) is a strong pair because |2 - 3| = 1 <= min(2, 3) = 2
  • (5, 10) is a strong pair because |5 - 10| = 5 <= min(5, 10) = 5
  • (1, 5) is NOT a strong pair because |1 - 5| = 4 > min(1, 5) = 1

The solution uses a sorted array approach with a binary trie data structure. By sorting the array and assuming x <= y, the strong pair condition simplifies to y <= 2x. This allows maintaining a sliding window of valid x values for each y, efficiently finding the maximum XOR using the trie structure.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

Let's think about the strong pair condition |x - y| <= min(x, y). The absolute value and minimum make this condition a bit complex to work with directly.

A key insight is that we can simplify this by ordering the pairs. Without loss of generality, let's assume x <= y. Then:

  • |x - y| = y - x (since y >= x)
  • min(x, y) = x (since x <= y)

So the condition becomes: y - x <= x, which simplifies to y <= 2x.

This is much cleaner! For any value y, we need to find all values x where x <= y and y <= 2x. Rearranging the second inequality: x >= y/2.

So for each y, valid x values must satisfy: y/2 <= x <= y.

Now, how do we efficiently find the maximum XOR? If we sort the array and process elements as y from smallest to largest, we can maintain a "window" of valid x candidates. As we move to a larger y:

  • We add y itself as a potential x candidate (since x can equal y)
  • We remove any x values that are now too small (where y > 2x)

This sliding window approach ensures we always have exactly the valid candidates for the current y.

For finding maximum XOR efficiently, a binary trie is perfect. It allows us to:

  • Insert numbers in O(log max_value) time
  • Remove numbers in O(log max_value) time
  • Find the maximum XOR with a given number in O(log max_value) time

The trie works by storing numbers in binary representation. When searching for maximum XOR with y, we greedily try to take the opposite bit at each level (if y has 0, we try to go to 1, and vice versa) to maximize the XOR result.

By combining the sorted array with sliding window and binary trie, we get an efficient solution that examines each element once as y and maintains only valid x candidates in the trie.

Learn more about Trie and Sliding Window patterns.

Solution Approach

The solution implements the sorted array + sliding window + binary trie approach:

1. Binary Trie Implementation

The [Trie](/problems/trie_intro) class maintains a binary representation of numbers with three key operations:

Insert Operation:

  • Inserts a number by traversing from the most significant bit (bit 20) to the least significant bit (bit 0)
  • For each bit position i, extracts the bit value using v = x >> i & 1
  • Creates new trie nodes as needed and increments the count at each node
  • The cnt field tracks how many numbers pass through each node

Search Operation:

  • Given a number x, finds the maximum XOR value possible with numbers in the trie
  • At each bit position, tries to take the opposite bit (v ^ 1) to maximize XOR
  • If the opposite bit path exists and has numbers (node.children[v ^ 1].cnt > 0), takes it and sets the corresponding bit in the answer
  • Otherwise, follows the same bit path
  • Returns the maximum XOR value achievable

Remove Operation:

  • Removes a number by traversing its bit path
  • Decrements the cnt at each node along the path
  • This effectively marks the number as removed without actually deleting nodes

2. Main Algorithm

def maximumStrongPairXor(self, nums: List[int]) -> int:
    nums.sort()  # Sort array to enable x <= y assumption
    tree = [Trie](/problems/trie_intro)()
    ans = i = 0  # ans stores max XOR, i is left pointer of window
  
    for y in nums:  # Enumerate each element as y
        tree.insert(y)  # Add y to valid candidates
      
        # Remove elements that violate y <= 2x (i.e., y > 2*nums[i])
        while y > nums[i] * 2:
            tree.remove(nums[i])
            i += 1
      
        # Find max XOR between y and valid candidates in trie
        ans = max(ans, tree.search(y))
  
    return ans

Key Steps:

  1. Sort the array: This ensures we can process elements in order and maintain the x <= y relationship

  2. Sliding window maintenance: For each y:

    • First insert y into the trie (it can pair with itself or future elements)
    • Remove elements from the left that no longer satisfy y <= 2x
    • The window [nums[i], ..., y] contains all valid x values for current y
  3. XOR maximization: Use the trie's search method to find the maximum XOR between y and any valid x in the window

Time Complexity: O(n log n + n * log M) where n is the array length and M is the maximum value (for bit operations)

  • Sorting: O(n log n)
  • For each element: O(log M) for insert, remove, and search operations

Space Complexity: O(n * log M) for the trie structure storing at most n numbers with log M bits each

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's trace through the algorithm with nums = [1, 2, 3, 4, 5].

Step 1: Sort the array Array is already sorted: [1, 2, 3, 4, 5]

Step 2: Initialize

  • tree = empty Trie
  • ans = 0 (maximum XOR found so far)
  • i = 0 (left pointer of sliding window)

Step 3: Process each element as y

When y = 1 (index 0):

  • Insert 1 into trie: tree = {1}
  • Check window: Is 1 > 2 * nums[0]? Is 1 > 2 * 1 = 2? No, so keep nums[0]
  • Valid x candidates in trie: {1}
  • Calculate XOR: 1 ⊕ 1 = 0
  • Update ans: max(0, 0) = 0

When y = 2 (index 1):

  • Insert 2 into trie: tree = {1, 2}
  • Check window: Is 2 > 2 * nums[0]? Is 2 > 2 * 1 = 2? No, so keep nums[0]
  • Valid x candidates in trie: {1, 2}
  • Calculate XORs: 2 ⊕ 1 = 3 (binary: 10 ⊕ 01 = 11), 2 ⊕ 2 = 0
  • Trie finds maximum: 3
  • Update ans: max(0, 3) = 3

When y = 3 (index 2):

  • Insert 3 into trie: tree = {1, 2, 3}
  • Check window: Is 3 > 2 * nums[0]? Is 3 > 2 * 1 = 2? Yes!
    • Remove nums[0] = 1 from trie: tree = {2, 3}
    • Increment i to 1
  • Valid x candidates in trie: {2, 3}
  • Calculate XORs: 3 ⊕ 2 = 1, 3 ⊕ 3 = 0
  • Trie finds maximum: 1
  • Update ans: max(3, 1) = 3

When y = 4 (index 3):

  • Insert 4 into trie: tree = {2, 3, 4}
  • Check window: Is 4 > 2 * nums[1]? Is 4 > 2 * 2 = 4? No, so keep nums[1]
  • Valid x candidates in trie: {2, 3, 4}
  • Calculate XORs: 4 ⊕ 2 = 6 (binary: 100 ⊕ 010 = 110), 4 ⊕ 3 = 7 (binary: 100 ⊕ 011 = 111), 4 ⊕ 4 = 0
  • Trie finds maximum: 7
  • Update ans: max(3, 7) = 7

When y = 5 (index 4):

  • Insert 5 into trie: tree = {2, 3, 4, 5}
  • Check window: Is 5 > 2 * nums[1]? Is 5 > 2 * 2 = 4? Yes!
    • Remove nums[1] = 2 from trie: tree = {3, 4, 5}
    • Increment i to 2
  • Check window: Is 5 > 2 * nums[2]? Is 5 > 2 * 3 = 6? No, so keep nums[2]
  • Valid x candidates in trie: {3, 4, 5}
  • Calculate XORs: 5 ⊕ 3 = 6, 5 ⊕ 4 = 1, 5 ⊕ 5 = 0
  • Trie finds maximum: 6
  • Update ans: max(7, 6) = 7

Result: The maximum XOR among all strong pairs is 7, achieved by the pair (3, 4).

Verification: Is (3, 4) a strong pair?

  • |3 - 4| = 1
  • min(3, 4) = 3
  • Is 1 ≤ 3? Yes! ✓

The trie efficiently finds the maximum XOR by trying to set opposite bits at each position. For example, when y = 4 (binary: 100), the trie searches for numbers that would maximize XOR by preferring paths with opposite bits, ultimately finding 3 (binary: 011) which gives XOR = 111 (decimal 7).

Solution Implementation

1from typing import List, Optional
2
3
4class Trie:
5    """Binary Trie data structure for XOR operations on integers."""
6  
7    __slots__ = ("children", "count")
8  
9    def __init__(self):
10        # Binary trie: children[0] for bit 0, children[1] for bit 1
11        self.children: List[Optional['Trie']] = [None, None]
12        # Count of numbers passing through this node
13        self.count: int = 0
14  
15    def insert(self, number: int) -> None:
16        """Insert a number into the trie by its binary representation.
17      
18        Args:
19            number: The integer to insert into the trie
20        """
21        current_node = self
22        # Process bits from most significant (bit 20) to least significant (bit 0)
23        for bit_position in range(20, -1, -1):
24            # Extract the bit at current position
25            bit_value = (number >> bit_position) & 1
26          
27            # Create new node if path doesn't exist
28            if current_node.children[bit_value] is None:
29                current_node.children[bit_value] = Trie()
30          
31            # Move to child node and increment count
32            current_node = current_node.children[bit_value]
33            current_node.count += 1
34  
35    def search(self, number: int) -> int:
36        """Find the maximum XOR value with the given number.
37      
38        Args:
39            number: The integer to find maximum XOR with
40          
41        Returns:
42            Maximum XOR value achievable with stored numbers
43        """
44        current_node = self
45        max_xor = 0
46      
47        # Process bits from most significant to least significant
48        for bit_position in range(20, -1, -1):
49            # Extract the bit at current position
50            bit_value = (number >> bit_position) & 1
51          
52            # Try to go opposite direction for maximum XOR
53            opposite_bit = bit_value ^ 1
54          
55            # Check if opposite path exists and has valid numbers
56            if current_node.children[opposite_bit] and current_node.children[opposite_bit].count:
57                # Set the bit in result and follow opposite path
58                max_xor |= (1 << bit_position)
59                current_node = current_node.children[opposite_bit]
60            else:
61                # Follow the same bit path if opposite doesn't exist
62                current_node = current_node.children[bit_value]
63      
64        return max_xor
65  
66    def remove(self, number: int) -> None:
67        """Remove a number from the trie by decrementing counts.
68      
69        Args:
70            number: The integer to remove from the trie
71        """
72        current_node = self
73        # Process bits from most significant to least significant
74        for bit_position in range(20, -1, -1):
75            # Extract the bit at current position
76            bit_value = (number >> bit_position) & 1
77          
78            # Move to child node and decrement count
79            current_node = current_node.children[bit_value]
80            current_node.count -= 1
81
82
83class Solution:
84    """Solution for finding maximum strong pair XOR."""
85  
86    def maximumStrongPairXor(self, nums: List[int]) -> int:
87        """Find the maximum XOR of a strong pair in the array.
88      
89        A strong pair (x, y) satisfies: |x - y| <= min(x, y)
90        Which is equivalent to: max(x, y) <= 2 * min(x, y)
91      
92        Args:
93            nums: List of positive integers
94          
95        Returns:
96            Maximum XOR value of any strong pair
97        """
98        # Sort array to maintain sliding window of valid pairs
99        nums.sort()
100      
101        # Initialize trie and tracking variables
102        trie = Trie()
103        max_xor_result = 0
104        left_pointer = 0
105      
106        # Process each number as the larger element in potential pairs
107        for current_num in nums:
108            # Add current number to trie
109            trie.insert(current_num)
110          
111            # Remove numbers that are too small to form strong pairs
112            # Strong pair condition: current_num <= 2 * nums[left_pointer]
113            while current_num > nums[left_pointer] * 2:
114                trie.remove(nums[left_pointer])
115                left_pointer += 1
116          
117            # Find maximum XOR with valid numbers in trie
118            max_xor_result = max(max_xor_result, trie.search(current_num))
119      
120        return max_xor_result
121
1/**
2 * Binary Trie data structure for storing and searching integers
3 * Uses binary representation to store numbers bit by bit
4 */
5class Trie {
6    // Array to store two children (0 and 1) for each bit position
7    private Trie[] children = new Trie[2];
8    // Counter to track how many numbers pass through this node
9    private int count = 0;
10
11    /**
12     * Constructor for Trie
13     */
14    public Trie() {
15    }
16
17    /**
18     * Insert a number into the trie by its binary representation
19     * @param number The integer to insert
20     */
21    public void insert(int number) {
22        Trie currentNode = this;
23        // Process bits from most significant to least significant (20th to 0th bit)
24        for (int bitPosition = 20; bitPosition >= 0; bitPosition--) {
25            // Extract the bit at current position (0 or 1)
26            int bitValue = (number >> bitPosition) & 1;
27          
28            // Create new node if path doesn't exist
29            if (currentNode.children[bitValue] == null) {
30                currentNode.children[bitValue] = new Trie();
31            }
32          
33            // Move to the child node
34            currentNode = currentNode.children[bitValue];
35            // Increment count for this path
36            currentNode.count++;
37        }
38    }
39
40    /**
41     * Search for the maximum XOR value with the given number
42     * @param number The integer to find maximum XOR with
43     * @return The maximum XOR value possible with numbers in the trie
44     */
45    public int search(int number) {
46        Trie currentNode = this;
47        int maxXorValue = 0;
48      
49        // Process bits from most significant to least significant
50        for (int bitPosition = 20; bitPosition >= 0; bitPosition--) {
51            // Extract the bit at current position
52            int bitValue = (number >> bitPosition) & 1;
53          
54            // Try to go opposite direction for maximum XOR
55            // XOR with opposite bit (0^1=1, 1^0=1) gives 1, which maximizes XOR
56            if (currentNode.children[bitValue ^ 1] != null && 
57                currentNode.children[bitValue ^ 1].count > 0) {
58                // Set the bit in result since we found opposite bit
59                maxXorValue |= (1 << bitPosition);
60                // Move to the opposite bit path
61                currentNode = currentNode.children[bitValue ^ 1];
62            } else {
63                // No opposite bit available, follow the same bit path
64                currentNode = currentNode.children[bitValue];
65            }
66        }
67      
68        return maxXorValue;
69    }
70
71    /**
72     * Remove a number from the trie by decrementing counts along its path
73     * @param number The integer to remove
74     */
75    public void remove(int number) {
76        Trie currentNode = this;
77      
78        // Traverse the path of the number and decrement counts
79        for (int bitPosition = 20; bitPosition >= 0; bitPosition--) {
80            // Extract the bit at current position
81            int bitValue = (number >> bitPosition) & 1;
82            // Move to the child node
83            currentNode = currentNode.children[bitValue];
84            // Decrement count for this path
85            currentNode.count--;
86        }
87    }
88}
89
90/**
91 * Solution class for finding maximum strong pair XOR
92 * A strong pair (x, y) satisfies: |x - y| <= min(x, y)
93 */
94class Solution {
95    /**
96     * Find the maximum XOR value among all strong pairs in the array
97     * @param nums The input array of integers
98     * @return The maximum XOR value of a strong pair
99     */
100    public int maximumStrongPairXor(int[] nums) {
101        // Sort array to efficiently maintain strong pair condition
102        Arrays.sort(nums);
103      
104        // Initialize trie and variables
105        Trie trie = new Trie();
106        int maxXor = 0;
107        int leftIndex = 0;
108      
109        // Iterate through sorted array
110        for (int currentNum : nums) {
111            // Add current number to trie
112            trie.insert(currentNum);
113          
114            // Remove numbers that violate strong pair condition
115            // For sorted array, strong pair condition becomes: y <= 2 * x
116            while (currentNum > nums[leftIndex] * 2) {
117                trie.remove(nums[leftIndex]);
118                leftIndex++;
119            }
120          
121            // Find maximum XOR with current number among valid strong pairs
122            maxXor = Math.max(maxXor, trie.search(currentNum));
123        }
124      
125        return maxXor;
126    }
127}
128
1class Trie {
2public:
3    Trie* children[2];  // Binary trie nodes for 0 and 1
4    int count;          // Count of numbers passing through this node
5  
6    Trie() : count(0) {
7        children[0] = nullptr;
8        children[1] = nullptr;
9    }
10  
11    // Insert a number into the trie by its binary representation
12    void insert(int num) {
13        Trie* currentNode = this;
14      
15        // Process bits from most significant (bit 20) to least significant (bit 0)
16        for (int bitPosition = 20; bitPosition >= 0; --bitPosition) {
17            // Extract the bit at current position
18            int bit = (num >> bitPosition) & 1;
19          
20            // Create new node if path doesn't exist
21            if (currentNode->children[bit] == nullptr) {
22                currentNode->children[bit] = new Trie();
23            }
24          
25            // Move to child node and increment count
26            currentNode = currentNode->children[bit];
27            ++currentNode->count;
28        }
29    }
30  
31    // Find maximum XOR value with given number among all numbers in trie
32    int search(int num) {
33        Trie* currentNode = this;
34        int maxXor = 0;
35      
36        // Process bits from most significant to least significant
37        for (int bitPosition = 20; bitPosition >= 0; --bitPosition) {
38            // Extract the bit at current position
39            int bit = (num >> bitPosition) & 1;
40          
41            // Try to go opposite direction for maximum XOR
42            // XOR with opposite bit (bit ^ 1) gives 1, maximizing XOR value
43            if (currentNode->children[bit ^ 1] != nullptr && 
44                currentNode->children[bit ^ 1]->count > 0) {
45                // Set this bit in result since we found opposite bit
46                maxXor |= (1 << bitPosition);
47                currentNode = currentNode->children[bit ^ 1];
48            } else {
49                // No opposite bit available, follow same bit path
50                currentNode = currentNode->children[bit];
51            }
52        }
53      
54        return maxXor;
55    }
56  
57    // Remove a number from the trie by decrementing counts along its path
58    void remove(int num) {
59        Trie* currentNode = this;
60      
61        // Process bits from most significant to least significant
62        for (int bitPosition = 20; bitPosition >= 0; --bitPosition) {
63            // Extract the bit at current position
64            int bit = (num >> bitPosition) & 1;
65          
66            // Move to child node and decrement count
67            currentNode = currentNode->children[bit];
68            --currentNode->count;
69        }
70    }
71};
72
73class Solution {
74public:
75    int maximumStrongPairXor(vector<int>& nums) {
76        // Sort array to maintain sliding window for strong pairs
77        sort(nums.begin(), nums.end());
78      
79        Trie* trie = new Trie();
80        int maxXorValue = 0;
81        int leftIndex = 0;  // Left boundary of valid strong pairs window
82      
83        // For each number as the larger element in a strong pair
84        for (int currentNum : nums) {
85            // Add current number to trie
86            trie->insert(currentNum);
87          
88            // Remove numbers that can't form strong pairs with current number
89            // Strong pair condition: |x - y| <= min(x, y)
90            // Since array is sorted and currentNum >= nums[leftIndex],
91            // we need currentNum - nums[leftIndex] <= nums[leftIndex]
92            // which means currentNum <= 2 * nums[leftIndex]
93            while (currentNum > nums[leftIndex] * 2) {
94                trie->remove(nums[leftIndex]);
95                ++leftIndex;
96            }
97          
98            // Find maximum XOR with current number among valid strong pairs
99            maxXorValue = max(maxXorValue, trie->search(currentNum));
100        }
101      
102        return maxXorValue;
103    }
104};
105
1// Trie node structure for binary representation
2interface TrieNode {
3    children: (TrieNode | null)[];  // Binary children: [0, 1]
4    count: number;                   // Number of values passing through this node
5}
6
7// Create a new Trie node
8function createTrieNode(): TrieNode {
9    return {
10        children: [null, null],
11        count: 0
12    };
13}
14
15// Global root of the Trie
16let trieRoot: TrieNode;
17
18// Insert a number into the Trie by its binary representation
19function insert(value: number): void {
20    let currentNode: TrieNode = trieRoot;
21  
22    // Process each bit from most significant to least significant
23    for (let bitPosition = 20; bitPosition >= 0; bitPosition--) {
24        // Extract the bit at current position
25        const bit = (value >> bitPosition) & 1;
26      
27        // Create child node if it doesn't exist
28        if (currentNode.children[bit] === null) {
29            currentNode.children[bit] = createTrieNode();
30        }
31      
32        // Move to child node and increment count
33        currentNode = currentNode.children[bit] as TrieNode;
34        currentNode.count++;
35    }
36}
37
38// Search for maximum XOR value with given number
39function search(value: number): number {
40    let currentNode: TrieNode = trieRoot;
41    let maxXorResult = 0;
42  
43    // Process each bit from most significant to least significant
44    for (let bitPosition = 20; bitPosition >= 0; bitPosition--) {
45        // Extract the bit at current position
46        const bit = (value >> bitPosition) & 1;
47      
48        // Try to go opposite direction for maximum XOR
49        const oppositeBit = bit ^ 1;
50      
51        // Check if opposite path exists and has values
52        if (currentNode.children[oppositeBit] !== null && 
53            (currentNode.children[oppositeBit] as TrieNode).count > 0) {
54            // Take opposite path for higher XOR value
55            maxXorResult |= (1 << bitPosition);
56            currentNode = currentNode.children[oppositeBit] as TrieNode;
57        } else {
58            // Take same path if opposite doesn't exist
59            currentNode = currentNode.children[bit] as TrieNode;
60        }
61    }
62  
63    return maxXorResult;
64}
65
66// Remove a number from the Trie by decrementing counts
67function remove(value: number): void {
68    let currentNode: TrieNode = trieRoot;
69  
70    // Process each bit from most significant to least significant
71    for (let bitPosition = 20; bitPosition >= 0; bitPosition--) {
72        // Extract the bit at current position
73        const bit = (value >> bitPosition) & 1;
74      
75        // Move to child node and decrement count
76        currentNode = currentNode.children[bit] as TrieNode;
77        currentNode.count--;
78    }
79}
80
81// Find maximum XOR value among strong pairs in array
82function maximumStrongPairXor(nums: number[]): number {
83    // Sort array in ascending order
84    nums.sort((a, b) => a - b);
85  
86    // Initialize Trie
87    trieRoot = createTrieNode();
88  
89    let maxXorValue = 0;
90    let leftPointer = 0;
91  
92    // Process each number as potential larger element in strong pair
93    for (const currentNum of nums) {
94        // Add current number to Trie
95        insert(currentNum);
96      
97        // Remove numbers that violate strong pair condition (y > 2*x)
98        while (currentNum > nums[leftPointer] * 2) {
99            remove(nums[leftPointer]);
100            leftPointer++;
101        }
102      
103        // Find maximum XOR with current number among valid pairs
104        maxXorValue = Math.max(maxXorValue, search(currentNum));
105    }
106  
107    return maxXorValue;
108}
109

Time and Space Complexity

Time Complexity: O(n × log M)

The algorithm sorts the array first, which takes O(n log n) time. Then for each element in the sorted array:

  • Insert operation: Traverses from bit position 20 to 0, taking O(log M) time where M = 2^20
  • Remove operation: May be called multiple times per iteration, but each element is removed at most once throughout the entire algorithm, contributing O(n × log M) total
  • Search operation: Traverses from bit position 20 to 0, taking O(log M) time

Since we iterate through n elements and perform operations that take O(log M) time each, and considering that log M = 20 (constant) while n can vary, the dominant term is O(n × log M). The sorting time O(n log n) is absorbed since log n ≤ log M for the given constraints.

Space Complexity: O(n × log M)

The Trie stores binary representations of numbers:

  • Each number requires a path of length log M = 20 bits from root to leaf
  • In the worst case, all n numbers are stored in the Trie simultaneously
  • Each unique path segment requires a new Trie node
  • Maximum space needed is proportional to the number of nodes, which is bounded by O(n × log M)

The sliding window approach ensures that at any time, only valid strong pairs are maintained in the Trie, but this doesn't change the worst-case space complexity analysis.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrect Strong Pair Condition Simplification

Pitfall: A common mistake is incorrectly simplifying the strong pair condition. Developers often assume that for sorted array with x <= y, the condition |x - y| <= min(x, y) becomes y - x <= x, leading to y <= 2x. However, they might forget to verify this works for all cases, especially when numbers can be negative or zero.

Why it happens: The mathematical transformation seems straightforward, but the condition relies on x being positive. With x <= y:

  • |x - y| = y - x (since y ≥ x)
  • min(x, y) = x
  • So the condition becomes: y - x <= x, which gives y <= 2x

Issue: This only works correctly when x > 0. If the array contains 0 or negative numbers, the logic breaks down.

Solution:

def maximumStrongPairXor(self, nums: List[int]) -> int:
    # Add validation for non-negative numbers
    if any(num < 0 for num in nums):
        raise ValueError("This implementation assumes non-negative numbers")
  
    # For arrays with 0, handle edge case
    nums = [num for num in nums if num > 0] or [0]
  
    nums.sort()
    # ... rest of the implementation

2. Bit Range Assumption in Trie

Pitfall: The trie implementation assumes numbers fit within 21 bits (processing from bit 20 to 0). If the input contains numbers larger than 2^21 - 1 (2,097,151), the trie will not correctly represent these numbers, leading to incorrect XOR calculations.

Why it happens: The code uses a hardcoded range range(20, -1, -1) without considering the actual maximum value in the input array.

Solution:

class Trie:
    def __init__(self, max_bits=21):
        self.children = [None, None]
        self.count = 0
        self.max_bits = max_bits
  
    def insert(self, number: int) -> None:
        current_node = self
        # Use dynamic bit range based on initialization
        for bit_position in range(self.max_bits - 1, -1, -1):
            bit_value = (number >> bit_position) & 1
            # ... rest of the logic

class Solution:
    def maximumStrongPairXor(self, nums: List[int]) -> int:
        if not nums:
            return 0
          
        # Calculate required bits based on maximum value
        max_val = max(nums)
        max_bits = max_val.bit_length() if max_val > 0 else 1
      
        nums.sort()
        trie = Trie(max_bits)  # Pass calculated bit count
        # ... rest of the implementation

3. Sliding Window Pointer Management

Pitfall: Not properly handling the case when all elements violate the strong pair condition with the current element. The while loop might cause left_pointer to exceed array bounds if not careful.

Why it happens: When processing a very large number y, all previous smaller numbers might need to be removed from the trie, potentially causing the left pointer to go beyond valid indices.

Solution:

def maximumStrongPairXor(self, nums: List[int]) -> int:
    nums.sort()
    trie = Trie()
    max_xor_result = 0
    left_pointer = 0
  
    for right_pointer, current_num in enumerate(nums):
        trie.insert(current_num)
      
        # Ensure left_pointer doesn't exceed right_pointer
        while left_pointer <= right_pointer and current_num > nums[left_pointer] * 2:
            trie.remove(nums[left_pointer])
            left_pointer += 1
      
        # Only search if trie has valid elements
        if left_pointer <= right_pointer:
            max_xor_result = max(max_xor_result, trie.search(current_num))
  
    return max_xor_result

4. Empty Trie Node Access

Pitfall: The search method doesn't handle the case where a child node is None, which can cause AttributeError when trying to access current_node.children[bit_value] after taking the opposite path.

Why it happens: After checking the opposite bit path and finding it doesn't exist or has count 0, the code assumes the same bit path exists, but this might not always be true if the trie is in an inconsistent state.

Solution:

def search(self, number: int) -> int:
    current_node = self
    max_xor = 0
  
    for bit_position in range(20, -1, -1):
        bit_value = (number >> bit_position) & 1
        opposite_bit = bit_value ^ 1
      
        # Check both paths for validity
        if current_node.children[opposite_bit] and current_node.children[opposite_bit].count > 0:
            max_xor |= (1 << bit_position)
            current_node = current_node.children[opposite_bit]
        elif current_node.children[bit_value] and current_node.children[bit_value].count > 0:
            current_node = current_node.children[bit_value]
        else:
            # Handle case where both paths are invalid
            return max_xor  # Return partial result
  
    return max_xor
Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

You are given an array of intervals where intervals[i] = [start_i, end_i] represent the start and end of the ith interval. You need to merge all overlapping intervals and return an array of the non-overlapping intervals that cover all the intervals in the input.


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More