XOR Queries of a Subarray

Updated on 01 July, 2025
XOR Queries of a Subarray header image

Problem Statement

In this challenge, you are provided with an array arr of positive integers and another array queries, where each element in queries is a pair [lefti, righti] indicating a subarray of arr from index lefti to righti. For each query, the task is to compute the XOR of the elements in the specified range of the array arr. The XOR operation between two numbers is a bitwise operation where bits that are different result in 1, and bits that are the same result in 0.

Your solution should return an array answer, where each element answer[i] contains the result of the XOR operation for the i-th query defined in queries.

Examples

Example 1

Input:

arr = [1,3,4,8], queries = [[0,1],[1,2],[0,3],[3,3]]

Output:

[2,7,14,8]

Explanation:

The binary representation of the elements in the array are:
1 = 0001
3 = 0011
4 = 0100
8 = 1000
The XOR values for queries are:
[0,1] = 1 xor 3 = 2
[1,2] = 3 xor 4 = 7
[0,3] = 1 xor 3 xor 4 xor 8 = 14
[3,3] = 8

Example 2

Input:

arr = [4,8,2,10], queries = [[2,3],[1,3],[0,0],[0,3]]

Output:

[8,0,4,4]

Constraints

  • 1 <= arr.length, queries.length <= 3 * 104
  • 1 <= arr[i] <= 109
  • queries[i].length == 2
  • 0 <= lefti <= righti < arr.length

Approach and Intuition

To solve the problem efficiently given the constraints, it's essential to handle the XOR operations over potentially large subarrays without recalculating from scratch for each query. Using direct computation for each query would inherently be time-consuming due to the large possible size of arr and queries. An optimal approach can leverage the properties of XOR and prefix sums:

  1. Precompute XOR Prefix: Create an auxiliary array prefixXOR such that prefixXOR[i] holds the XOR of all elements from arr[0] to arr[i]. This can be calculated in a single pass over arr.

    • For i = 0, set prefixXOR[i] = arr[i].
    • For i > 0, set prefixXOR[i] = prefixXOR[i-1] ^ arr[i] (where ^ denotes the XOR operation).
  2. Answer Queries Using PrefixXOR:

    • If the query is to calculate the XOR from lefti to righti, the result can be derived from prefixXOR.
    • Specifically, if lefti is 0, the result is simply prefixXOR[righti] as it represents the XOR from the start to righti.
    • If lefti is greater than 0, use the property prefixXOR[righti] ^ prefixXOR[lefti - 1]. This operation effectively cancels out the XOR contributions from elements before lefti, providing the XOR from lefti to righti.

This approach is efficient because it reduces the time complexity to compute each query to O(1) after an O(n) preprocessing step for the XOR prefix array, making it suitable given the problem constraints.

Solutions

  • C++
  • Java
  • Python
cpp
class Solution {
public:
    vector<int> xorQueries(vector<int>& data, vector<vector<int>>& questions) {
        vector<int> output;
    
        for (int i = 1; i < data.size(); ++i) {
            data[i] ^= data[i - 1];
        }
    
        for (const auto& question : questions) {
            if (question[0] > 0) {
                output.push_back(data[question[0] - 1] ^ data[question[1]]);
            } else {
                output.push_back(data[question[1]]);
            }
        }
    
        return output;
    }
};

This C++ solution addresses the problem of executing XOR queries on a subarray. The function xorQueries uses a prefix XOR array to efficiently calculate the result for each query. Follow these steps to understand the approach implemented in the code:

  1. Initialize a vector output to store the results of queries.
  2. Convert the input data vector into a prefix XOR array. This transformation helps in retrieving the XOR of any subarray in constant time. For each index from 1 to the size of data, update data[i] with the XOR of data[i] and data[i-1].
  3. Loop through each query in questions. Determine the result based on the range given:
    • If the start of the query range question[0] is greater than 0, calculate the XOR from data[question[0] - 1] to data[question[1]] using the prefix XOR property and push the result to output.
    • If question[0] is 0, directly push data[question[1]] to output as it represents the XOR from the beginning of the array up to question[1].
  4. Return the vector output which contains the result for each query.

The use of a prefix XOR array enhances the efficiency of the solution, enabling each query result to be computed in constant time after the initial preprocessing step.

java
class Solution {
    
    public int[] xorQueries(int[] arr, int[][] queries) {
        List<Integer> resList = new ArrayList<>();
    
        // Creating prefix XOR representation for arr
        for (int i = 1; i < arr.length; i++) {
            arr[i] ^= arr[i - 1];
        }
    
        // Evaluate queries using the prefix XOR
        for (int[] query : queries) {
            if (query[0] > 0) {
                resList.add(arr[query[0] - 1] ^ arr[query[1]]);
            } else {
                resList.add(arr[query[1]]);
            }
        }
    
        return resList.stream().mapToInt(i -> i).toArray();
    }
}

The proposed Java solution revolves around solving the "XOR Queries of a Subarray" problem by utilizing the prefix XOR array concept. Here's a concise explanation of the approach adopted in the code:

  1. Convert the input array into a prefix XOR array. This involves iterating through the array such that each element at index i becomes the XOR of all elements from the beginning of the array up to i. This is performed using an in-place modification for efficiency.

  2. Process each query based on the prefix XOR array prepared in the previous step. Each query specifies a subarray using a start and end index. If the start index is more than 0, find the XOR for the subarray by XORing elements at positions defined by the query. If the start index is 0, directly take the value from the array since the XOR from start up to any index i in a prefix XOR array is directly stored at arr[i].

  3. Convert the resultant list from query evaluations into an array of integers to match the expected output format.

This method is efficient, leveraging the properties of XOR and the cumulation nature of the prefix array to answer each query in constant time after the initial pre-processing step, resulting in a time complexity of O(N + Q) where N is the number of elements in the array, and Q is the number of queries.

python
class Solution:
    def computeXorQueries(self, data, query_list):
        query_results = []
    
        # Convert data list into a prefix XOR accumulation
        for idx in range(1, len(data)):
            data[idx] ^= data[idx - 1]
    
        # Handle each query with the accumulated XOR values
        for start, end in query_list:
            if start > 0:
                query_results.append(data[start - 1] ^ data[end])
            else:
                query_results.append(data[end])
    
        return query_results

The Python solution for XOR Queries of a Subarray involves leveraging the properties of XOR and prefix accumulation to efficiently answer multiple queries regarding the XOR of subarray elements. Follow the detailed steps included in the implementation:

  1. Initiate an empty list query_results to store the results of each query.

  2. Create a prefix XOR accumulation for the given list data. This step is crucial as it enables each element at index i in data to store the XOR of all elements from the start up to i. Modify the data list in-place starting from index 1 through the length of data. For each index, update the current element to be XORed with the previous element.

  3. Process each query in query_list. Each query is defined by a pair of indices (start, end). For each query:

    • If start is greater than 0, compute the XOR from index start to end by XORing the elements at data[end] and data[start - 1] (because of the properties of prefix accumulation). Append the result to query_results.
    • If start equals 0, append the value of data[end] directly to query_results as this covers the XOR from the beginning up to the end.
  4. Return the query_results as the output of the function.

This approach ensures that each query is handled in constant time, O(1), after the initial setup of the prefix XOR list, making the entire solution efficient, especially for scenarios with multiple queries.

Comments

No comments yet.