2935. Maximum Strong Pair XOR II
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:
- Select two integers from
nums
that form a strong pair - Calculate their bitwise XOR
- 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.
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
(sincey >= x
)min(x, y) = x
(sincex <= 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 potentialx
candidate (sincex
can equaly
) - We remove any
x
values that are now too small (wherey > 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 usingv = 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:
-
Sort the array: This ensures we can process elements in order and maintain the
x <= y
relationship -
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 validx
values for currenty
- First insert
-
XOR maximization: Use the trie's
search
method to find the maximum XOR betweeny
and any validx
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 EvaluatorExample 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]
? Is1 > 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]
? Is2 > 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]
? Is3 > 2 * 1 = 2
? Yes!- Remove nums[0] = 1 from trie:
tree = {2, 3}
- Increment i to 1
- Remove nums[0] = 1 from trie:
- 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]
? Is4 > 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]
? Is5 > 2 * 2 = 4
? Yes!- Remove nums[1] = 2 from trie:
tree = {3, 4, 5}
- Increment i to 2
- Remove nums[1] = 2 from trie:
- Check window: Is
5 > 2 * nums[2]
? Is5 > 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 whereM = 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 givesy <= 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
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
Trie Introduction Imagine you have a magic dictionary where as soon as you start spelling a word it immediately tells you how many words that start with those letters How cool would that be This is exactly the kind of magic a data structure called trie performs What's a Trie
https assets algo monster cover_photos stack svg Sliding Window Maximum Monotonic Stack We have an array and a sliding window defined by a start index and an end index The sliding window moves from left of the array to right There are always k elements in the window The window
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!