Skip to main content

LeetCode 1829: Maximum XOR for Each Query – Step-by-Step Guide to Optimizing the Solution

This problem involves maximizing the XOR operation across multiple queries, making it an interesting and challenging problem.

In this blog post, we’ll walk through how to approach LeetCode 1829, starting with a brute-force solution and gradually optimizing it for better performance. We’ll break down the problem, explain the brute-force method, and then introduce an optimized approach that significantly improves efficiency.

Problem

You are given an array of non-negative integers (nums) sorted in ascending order and an integer maximumBit. Your task is to perform n queries (where n is the size of the array) and for each query, find a non-negative integer k that maximizes the XOR result when combined with all elements of the array up to that point.

For each query:

  1. You XOR all elements of the array up to the current query.
  2. You find a k (where 0 <= k < 2^maximumBit) such that the result of XOR-ing all elements with k is maximized.

After each query, the last element is removed from the array and the process continues until you have processed all queries.

Let's take an example to understand how this works:

Example 1:

  • Input: nums = [0, 1, 1, 3], maximumBit = 2

    For each query, we want to maximize the XOR result. Here's how we would process the queries:

  1. Query 1: With nums = [0, 1, 1, 3], we find that k = 0 since 0 XOR 1 XOR 1 XOR 3 XOR 0 = 3.
  2. Query 2: After removing the last element, nums = [0, 1, 1], we find k = 3 since 0 XOR 1 XOR 1 XOR 3 = 3.
  3. Query 3: After removing another element, nums = [0, 1], we find k = 2 since 0 XOR 1 XOR 2 = 3.
  4. Query 4: Finally, with nums = [0], we find k = 3 since 0 XOR 3 = 3.

Output: [0, 3, 2, 3]


Brute-Force Approach

The brute-force solution for this problem involves iterating over every possible value of k for each query and calculating the XOR of all the elements in the array up to that query. While this approach is easy to understand, it's not efficient enough for large inputs (with n up to 100,000).

Step-by-Step Brute Force Solution

class Solution {
public:
    vector<int> getMaximumXor(vector<int>& nums, int maximumBit)
    {
        int n = nums.size();
        vector<int> ans(n);
        int maxK = (1 << maximumBit) - 1;  // Calculate the maximum possible value of k
       
        for (int i = n - 1; i >= 0; --i)
        {
            int currentXor = 0;
            // XOR all elements in the array up to the current index i
            for (int j = 0; j <= i; ++j)
            {
                currentXor ^= nums[j];
            }
           
            // Try all possible k values from 0 to maxK
            int bestK = 0;
            for (int k = 0; k <= maxK; ++k)
            {
                if ((currentXor ^ k) > (currentXor ^ bestK))
                {
                    bestK = k;
                }
            }
            ans[i] = bestK;
        }
       
        return ans;
    }
};

Time Complexity of the Brute Force Approach

  • Outer Loop: We process n queries, so we iterate over the array n times.
  • Inner Loop (Array Traversal): For each query, we compute the XOR of all elements in the array up to the current query, which takes O(n) time.
  • Inner Inner Loop (K values): For each query, we check all possible values of k (from 0 to 2^maximumBit - 1), which requires up to 2^maximumBit checks.

Thus, the overall time complexity is O(n^2 * 2^maximumBit), which is inefficient for large input sizes.


Optimized Approach

The brute-force solution is too slow, especially when n is large. To optimize this, we can take advantage of the cumulative XOR and process the queries in reverse order.

Key Insight for Optimization:

  1. Cumulative XOR: Instead of recalculating the XOR for each element every time, we can maintain a running cumulative XOR of all elements up to the current point.
  2. Reverse Query Processing: Since the queries require removing the last element of nums after each step, processing the queries in reverse order helps reuse previous computations.

Optimized Algorithm

class Solution {
public:
    void solve(vector<int>& nums, vector<int>& ans)
    {
        int n = nums.size();
        ans[n - 1] = nums[0]; // Initial XOR for the last element
       
        // Compute cumulative XOR in reverse order
        for (int i = n - 2; i >= 0; i--)
        {
            ans[i] = ans[i + 1] ^ nums[n - i - 1];
        }
    }

    vector<int> getMaximumXor(vector<int>& nums, int maximumBit)
    {
        vector<int> ans(nums.size());
        int k = (1 << maximumBit) - 1; // Calculate the maximum value of k
       
        // Compute cumulative XOR array
        solve(nums, ans);

        // Perform XOR operation with k for each query
        for (int i = 0; i < nums.size(); i++)
        {
            ans[i] = ans[i] ^ k;
        }
       
        return ans;
    }
};

Explanation of the Optimized Solution

  1. Cumulative XOR Calculation:

    • We initialize ans[n-1] with nums[0]. Then, we iterate backward through the array and calculate the cumulative XOR for each element.
  2. Maximizing XOR with k:

    • Once we have the cumulative XOR for each query, we calculate the maximum k by performing a bitwise XOR with k = 2^maximumBit - 1.
  3. Efficient Query Processing:

    • By computing the cumulative XOR in reverse and using the XOR result directly, we avoid recomputing the XOR for each query, resulting in a more efficient solution.

Time Complexity of the Optimized Approach

  • Cumulative XOR Calculation: This takes O(n) time as we only need to traverse the array once.

  • Final XOR Calculation with k: This also takes O(n) time.

  • Therefore, the overall time complexity is O(n), which is a significant improvement over the brute-force approach.

  • Space Complexity: We store the results in the ans array, so the space complexity is O(n).


Why the Optimized Approach Works

The optimized solution is far more efficient because:

  1. It avoids recalculating the XOR for the entire array on each query by using cumulative XOR.
  2. The solution processes the queries in reverse order, leveraging previously computed results, thus reducing the time complexity to O(n), compared to the brute-force method's O(n^2 * 2^maximumBit).

This makes the optimized approach ideal for handling large arrays (up to 100,000 elements) as specified in the problem's constraints.


LeetCode 1829 is a great problem for practicing bit manipulation and learning how to optimize solutions using cumulative operations. By starting with a brute-force approach and gradually moving to an optimized solution, we can efficiently solve the problem with a time complexity of O(n).

The key to solving this problem efficiently lies in the observation that you can compute the XOR values incrementally using cumulative XOR and reduce the overall computational complexity significantly.

Popular posts from this blog

Top 10 Beginner-Friendly LeetCode Questions and Their Solutions

If you're new to solving coding problems on LeetCode, it can feel overwhelming. Where do you start? Which problems are suitable for beginners? Don’t worry! In this blog post, I’ll guide you through 10 beginner-friendly LeetCode questions that are perfect for getting started on your coding journey. These problems will help you build confidence, improve your problem-solving skills, and lay a solid foundation in data structures and algorithms. Why Start with Beginner-Friendly Problems? Before diving into advanced topics like dynamic programming or graph theory, it’s essential to: Build a strong foundation in basic programming concepts. Understand how to approach a coding problem methodically. Gain familiarity with LeetCode’s platform and its problem structure. The following problems are simple yet impactful, designed to introduce you to common techniques like loops, arrays, strings, and basic math operations. 10 Beginner-Friendly LeetCode Problems 1. Two Sum (Easy) Problem Link : Two...

LeetCode 2583: Kth Largest Sum in a Binary Tree – Solution Explained

When working with binary trees, one common task is to analyze the properties of the tree's levels. In this blog post, we'll walk through a solution to LeetCode Problem 2583: Kth Largest Sum in a Binary Tree , a problem that challenges us to compute and find specific sums from different levels of a binary tree. Problem Statement You are given the root of a binary tree and a positive integer k . Your goal is to return the k-th largest level sum in the tree. The level sum is defined as the sum of the values of all nodes at the same depth in the tree. If the number of levels in the tree is less than k , the function should return -1 . Example 1: Input: root = [5, 8, 9, 2, 1, 3, 7, 4, 6] , k = 2 Output: 13 Explanation: Level 1 sum: 5 Level 2 sum: 8 + 9 = 17 Level 3 sum: 2 + 1 + 3 + 7 = 13 Level 4 sum: 4 + 6 = 10 The 2nd largest sum is 13 . Example 2: Input: root = [1, 2, null, 3] , k = 1 Output: 3 Explanation: The largest level sum is 3 (at the third level). This problem essentia...

LeetCode 3370: Smallest Number With All Set Bits – Problem Explanation and Solutions

Are you looking to master bit manipulation and tackle interesting coding challenges? In this post, we’ll explore LeetCode Problem 3370: Smallest Number With All Set Bits . We’ll dive deep into the problem statement, break down a brute force approach, and finally discuss an optimized solution. If you’re preparing for technical interviews or just love solving algorithmic problems, this guide is for you! Problem Statement: Smallest Number With All Set Bits You are given a positive integer n . Your task is to find the smallest number x such that: x is greater than or equal to n . The binary representation of x consists only of set bits ( 1 s). Examples: Example 1: Input: n = 5 Output: 7 Explanation: The binary representation of 7 is 111 , which is the smallest number greater than or equal to 5 with all bits set. Example 2: Input: n = 10 Output: 15 Explanation: The binary representation of 15 is 1111 . Example 3: Input: n = 3 Output: 3 Explanation: The binary representation of 3 is...