Facebook Pixel

3277. Maximum XOR Score Subarray Queries


Problem Description

You are given an array nums of n integers, and a 2D integer array queries of size q, where queries[i] = [l_i, r_i].

For each query, you must find the maximum XOR score of any subarray of nums[l_i..r_i].

The XOR score of an array a is determined by repeatedly applying the following operations until one element remains, which is the score:

  • Simultaneously replace a[i] with a[i] XOR a[i + 1] for all indices i except the last one.
  • Remove the last element of a.

Return an array answer of size q where answer[i] is the result of query i.

Intuition

To solve this problem, we need to efficiently calculate the maximum XOR score for each query subarray. The approach involves breaking down the problem using dynamic programming.

  1. Define Dynamic Programming States:

    • Let f[i][j] represent the XOR value of the subarray nums[i..j].
    • We derive the relation: f[i][j] = f[i][j-1] ^ f[i+1][j], where ^ denotes the XOR operation.
  2. Define Maximum XOR State:

    • Introduce g[i][j] to represent the maximum value obtainable for XOR scores over nums[i..j].
    • Transition occurs as: g[i][j] = max(f[i][j], g[i][j-1], g[i+1][j]).
  3. Answer Queries:

    • By precomputing the DP table for g, each query can be resolved in constant time by retrieving g[l][r] for the specific indices.

This method efficiently answers each query by leveraging precomputed results in the dynamic programming table and avoids recalculating XOR scores from scratch for overlapping subarrays.

Learn more about Dynamic Programming patterns.

Solution Approach

The solution to finding the maximum XOR score for each query involves utilizing a dynamic programming approach to precompute potential results for all subarrays, followed by efficiently answering each query using these precomputed values.

  1. Dynamic Programming Tables:

    • We define two 2D arrays, f and g, where both are of size n x n and n is the length of nums.
    • f[i][j] is used to compute the XOR value for all elements from nums[i] to nums[j].
    • g[i][j] stores the maximum XOR value for any subarray from nums[i] to nums[j].
  2. Initialization and Iteration:

    • Initialize the diagonal elements of the DP tables as f[i][i] = g[i][i] = nums[i] for all i since a single element subarray has its XOR score as the element itself.
    • Iterate through the array in a reverse order, from n-1 to 0, ensuring that partial results are built while respecting dependencies among indices.
  3. State Transitions:

    • For determining f[i][j], use the relation:
      f[i][j] = f[i][j-1] ^ f[i+1][j]
      This equation is based on combining previous results to form new XOR combinations.
    • For g[i][j], update using:
      g[i][j] = max(f[i][j], g[i][j-1], g[i+1][j])
      Here, the maximum value is derived from either the newly computed XOR score or previously calculated maximum scores that may extend into overlapping subarrays.
  4. Answering Queries:

    • With the table g fully populated, respond to each query in queries swiftly by accessing g[l][r], thus ensuring an efficient retrieval of results with minimal additional computation.

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

Let's consider a small example to illustrate the solution approach:

Input:

  • Array nums: [1, 2, 3, 4]
  • Queries queries: [[0, 2], [1, 3]]

Objective:

For each query [l, r], find the maximum XOR score for the subarray nums[l..r].

Step-by-Step Solution:

  1. Initialization:

    • Set up two 2D tables f and g with dimensions n x n, where n is the length of nums.
    • For diagonal elements, initialize:
      f[i][i] = g[i][i] = nums[i]
      So, f and g would initially look like this for our example:
      f = g = [
        [1, 0, 0, 0],
        [0, 2, 0, 0],
        [0, 0, 3, 0],
        [0, 0, 0, 4]
      ]
  2. Populate DP Tables:

    • Compute f[i][j] and g[i][j] for each subarray nums[i..j]:
      • For j = 1:
        • f[0][1] = f[0][0] ^ f[1][1] = 1 ^ 2 = 3
        • f[1][2] = f[1][1] ^ f[2][2] = 2 ^ 3 = 1
        • f[2][3] = f[2][2] ^ f[3][3] = 3 ^ 4 = 7
        • Similarly, update g using:
          g[0][1] = max(f[0][1], g[0][0], g[1][1]) = max(3, 1, 2) = 3
          And similar updates for other g[i][j].
      • Continue similar calculations for j = 2, j = 3 to fill out f and g.
  3. State Transition:

    • Sample calculations for higher indices:
      • j = 2, f[0][2] = f[0][1] ^ f[1][2] = 3 ^ 1 = 2
      • g[0][2] = max(f[0][2], g[0][1], g[1][2]) = max(2, 3, 1) = 3
      • j = 3, f[0][3] = f[0][2] ^ f[1][3], continue the process for complete tables.
  4. Answer Queries:

    • For each query in queries, retrieve the precomputed maximum XOR score from g:
      • Query [0, 2]: Result is g[0][2] = 3
      • Query [1, 3]: Compute this or retrieve if precomputed: g[1][3] = max possible XOR score from subarray [2, 3, 4] = 7

Final Output:

  • Resulting array answer: [3, 7]

Solution Implementation

1from typing import List
2
3class Solution:
4    def maximumSubarrayXor(self, nums: List[int], queries: List[List[int]]) -> List[int]:
5        n = len(nums)
6
7        # Initialize 2D arrays to store XOR results and maximum XORs.
8        xor_results = [[0] * n for _ in range(n)]
9        max_xor = [[0] * n for _ in range(n)]
10
11        # Fill the tables following the logic of dynamic programming
12        for i in range(n - 1, -1, -1):
13            xor_results[i][i] = max_xor[i][i] = nums[i]
14            for j in range(i + 1, n):
15                # Calculate XOR for subarray nums[i...j]
16                xor_results[i][j] = xor_results[i][j - 1] ^ xor_results[i + 1][j]
17                # Update maximum XOR for the subarray nums[i...j]
18                max_xor[i][j] = max(xor_results[i][j], max_xor[i][j - 1], max_xor[i + 1][j])
19      
20        # Process each query and return the result list
21        return [max_xor[l][r] for l, r in queries]
22
1class Solution {
2    public int[] maximumSubarrayXor(int[] nums, int[][] queries) {
3        int n = nums.length;
4      
5        // Create 2D arrays to store XOR results (xorArray) and maximum XOR results (maxXorArray) for subarrays.
6        int[][] xorArray = new int[n][n];
7        int[][] maxXorArray = new int[n][n];
8      
9        // Calculate XOR and maximum XOR for subarrays.
10        for (int i = n - 1; i >= 0; --i) {
11            xorArray[i][i] = nums[i];
12            maxXorArray[i][i] = nums[i];
13            for (int j = i + 1; j < n; ++j) {
14                xorArray[i][j] = xorArray[i][j - 1] ^ xorArray[i + 1][j];
15                maxXorArray[i][j] = Math.max(xorArray[i][j], Math.max(maxXorArray[i][j - 1], maxXorArray[i + 1][j]));
16            }
17        }
18      
19        int m = queries.length;
20        int[] results = new int[m];
21      
22        // Answer each query by fetching the maximum XOR for the queried subarray.
23        for (int i = 0; i < m; ++i) {
24            int left = queries[i][0];
25            int right = queries[i][1];
26            results[i] = maxXorArray[left][right];
27        }
28      
29        return results;
30    }
31}
32
1#include <vector>
2#include <algorithm>
3using namespace std;
4
5class Solution {
6public:
7    vector<int> maximumSubarrayXor(vector<int>& nums, vector<vector<int>>& queries) {
8        int n = nums.size(); // Size of the input array
9        vector<vector<int>> xorSubarray(n, vector<int>(n)); // 2D vector to store XOR of subarrays
10        vector<vector<int>> maxXor(n, vector<int>(n)); // 2D vector to store the max XOR value of subarrays
11      
12        // Fill the xorSubarray and maxXor arrays
13        for (int i = n - 1; i >= 0; --i) {
14            xorSubarray[i][i] = nums[i]; // Each element is a subarray itself, XOR with itself is the element
15            maxXor[i][i] = nums[i]; // The max XOR for a single element is the element itself
16            for (int j = i + 1; j < n; ++j) {
17                // XOR of subarray from i to j
18                xorSubarray[i][j] = xorSubarray[i][j - 1] ^ xorSubarray[i + 1][j];
19                // Update the maximum XOR for the subarray by comparing with previous maximums
20                maxXor[i][j] = max({xorSubarray[i][j], maxXor[i][j - 1], maxXor[i + 1][j]});
21            }
22        }
23      
24        vector<int> result; // Vector to store the results for each query
25        for (const auto& query : queries) {
26            int left = query[0], right = query[1];
27            result.push_back(maxXor[left][right]); // Append the maximum XOR value for the range to the result
28        }
29      
30        return result; // Return final results for all queries
31    }
32};
33
1// This function calculates the maximum XOR for subarrays in response to given queries.
2function maximumSubarrayXor(nums: number[], queries: number[][]): number[] {
3    const n = nums.length;
4
5    // Initialize 2D arrays f and g with default values of 0.
6    const f: number[][] = Array.from({ length: n }, () => Array(n).fill(0));
7    const g: number[][] = Array.from({ length: n }, () => Array(n).fill(0));
8
9    // Traverse the array backwards to build the table of XOR and maximum XOR values.
10    for (let i = n - 1; i >= 0; i--) {
11        // Base case: Single element XOR is the element itself.
12        f[i][i] = nums[i];
13        g[i][i] = nums[i];
14      
15        // Calculate XOR and maximum XOR for subarrays starting at index i.
16        for (let j = i + 1; j < n; j++) {
17            // XOR of elements from i to j
18            f[i][j] = f[i][j - 1] ^ f[i + 1][j];
19            // Maximum XOR for subarrays from i to j
20            g[i][j] = Math.max(f[i][j], Math.max(g[i][j - 1], g[i + 1][j]));
21        }
22    }
23
24    // Answer each query by returning the precomputed maximum XOR from index l to r.
25    return queries.map(([l, r]) => g[l][r]);
26}
27

Time and Space Complexity

The time complexity of the code is O(n^2 + m). This is because the code contains a nested loop where i varies from n-1 to 0 and j varies from i+1 to n, leading to O(n^2) operations to fill matrices f and g. Additionally, generating the output array by accessing g[l][r] for each query takes O(m) time, where m is the number of queries.

The space complexity is O(n^2). This is due to the use of two n x n matrices f and g, which require O(n^2) space.

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


Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:
Question 1 out of 10

Consider the classic dynamic programming of fibonacci numbers, what is the recurrence relation?


Recommended Readings

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


Load More