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]
witha[i] XOR a[i + 1]
for all indicesi
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.
-
Define Dynamic Programming States:
- Let
f[i][j]
represent the XOR value of the subarraynums[i..j]
. - We derive the relation:
f[i][j] = f[i][j-1] ^ f[i+1][j]
, where^
denotes the XOR operation.
- Let
-
Define Maximum XOR State:
- Introduce
g[i][j]
to represent the maximum value obtainable for XOR scores overnums[i..j]
. - Transition occurs as:
g[i][j] = max(f[i][j], g[i][j-1], g[i+1][j])
.
- Introduce
-
Answer Queries:
- By precomputing the DP table for
g
, each query can be resolved in constant time by retrievingg[l][r]
for the specific indices.
- By precomputing the DP table for
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.
-
Dynamic Programming Tables:
- We define two 2D arrays,
f
andg
, where both are of sizen x n
andn
is the length ofnums
. f[i][j]
is used to compute the XOR value for all elements fromnums[i]
tonums[j]
.g[i][j]
stores the maximum XOR value for any subarray fromnums[i]
tonums[j]
.
- We define two 2D arrays,
-
Initialization and Iteration:
- Initialize the diagonal elements of the DP tables as
f[i][i] = g[i][i] = nums[i]
for alli
since a single element subarray has its XOR score as the element itself. - Iterate through the array in a reverse order, from
n-1
to0
, ensuring that partial results are built while respecting dependencies among indices.
- Initialize the diagonal elements of the DP tables as
-
State Transitions:
- For determining
f[i][j]
, use the relation:
This equation is based on combining previous results to form new XOR combinations.f[i][j] = f[i][j-1] ^ f[i+1][j]
- For
g[i][j]
, update using:
Here, the maximum value is derived from either the newly computed XOR score or previously calculated maximum scores that may extend into overlapping subarrays.g[i][j] = max(f[i][j], g[i][j-1], g[i+1][j])
- For determining
-
Answering Queries:
- With the table
g
fully populated, respond to each query inqueries
swiftly by accessingg[l][r]
, thus ensuring an efficient retrieval of results with minimal additional computation.
- With the table
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample 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:
-
Initialization:
- Set up two 2D tables
f
andg
with dimensionsn x n
, wheren
is the length ofnums
. - For diagonal elements, initialize:
So,f[i][i] = g[i][i] = nums[i]
f
andg
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] ]
- Set up two 2D tables
-
Populate DP Tables:
- Compute
f[i][j]
andg[i][j]
for each subarraynums[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:
And similar updates for otherg[0][1] = max(f[0][1], g[0][0], g[1][1]) = max(3, 1, 2) = 3
g[i][j]
.
- Continue similar calculations for
j = 2
,j = 3
to fill outf
andg
.
- For
- Compute
-
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.
- Sample calculations for higher indices:
-
Answer Queries:
- For each query in
queries
, retrieve the precomputed maximum XOR score fromg
:- 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
- Query [0, 2]: Result is
- For each query in
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.
Consider the classic dynamic programming of fibonacci numbers, what is the recurrence relation?
Recommended Readings
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
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
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Want a Structured Path to Master System Design Too? Don’t Miss This!