Facebook Pixel

1720. Decode XORed Array

EasyBit ManipulationArray
Leetcode Link

Problem Description

You have a hidden integer array arr containing n non-negative integers. This array has been encoded into another array called encoded with length n - 1.

The encoding works as follows: each element encoded[i] is calculated by taking the XOR of two consecutive elements from the original array: encoded[i] = arr[i] XOR arr[i + 1].

For example, if the original array is arr = [1,0,2,1], then the encoded array would be encoded = [1,2,3] because:

  • encoded[0] = 1 XOR 0 = 1
  • encoded[1] = 0 XOR 2 = 2
  • encoded[2] = 2 XOR 1 = 3

You are given:

  1. The encoded array
  2. An integer first, which is the first element of the original array (arr[0])

Your task is to reconstruct and return the original array arr.

The key insight is that if you know arr[i] and encoded[i], you can find arr[i+1] using the XOR property. Since encoded[i] = arr[i] XOR arr[i+1], you can rearrange this to get arr[i+1] = arr[i] XOR encoded[i]. This works because XOR has the property that a XOR a = 0 and 0 XOR b = b.

Starting with first as arr[0], you can sequentially compute each subsequent element of the original array by XORing the current element with the corresponding encoded value.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key to solving this problem lies in understanding the properties of the XOR operation. XOR has a special characteristic: it's its own inverse operation. This means if a XOR b = c, then a XOR c = b and b XOR c = a.

Let's think about what we know:

  • We have encoded[i] = arr[i] XOR arr[i+1]
  • We know arr[0] (which is first)
  • We need to find all elements of arr

Since we know arr[0] and encoded[0], can we find arr[1]? Let's manipulate the equation:

If encoded[0] = arr[0] XOR arr[1], and we XOR both sides with arr[0]:

  • Left side: arr[0] XOR encoded[0]
  • Right side: arr[0] XOR (arr[0] XOR arr[1])

The right side simplifies because arr[0] XOR arr[0] = 0, and 0 XOR arr[1] = arr[1].

So we get: arr[1] = arr[0] XOR encoded[0]

This pattern continues! Once we have arr[1], we can find arr[2] using arr[2] = arr[1] XOR encoded[1], and so on.

It's like a chain reaction - each element of the original array can be recovered by XORing the previous element with the corresponding encoded value. We start with the given first element and propagate forward through the entire array, decoding one element at a time.

This works because XOR essentially "locks" two values together in the encoded array, and knowing one value allows us to "unlock" the other using the same XOR operation.

Solution Approach

Based on our intuition, we can implement the solution by iteratively applying the XOR operation to decode each element of the original array.

The mathematical relationship we derived is:

arr[i + 1] = arr[i] XOR encoded[i]

Here's how we implement this step by step:

  1. Initialize the result array: Start with an array containing just the first element, since arr[0] = first is given.

  2. Iterate through the encoded array: For each element x in encoded, we calculate the next element of the original array.

  3. Apply the XOR operation: For each encoded[i], compute arr[i + 1] = arr[i] XOR encoded[i]. In the code, this is done with ans[-1] ^ x, where:

    • ans[-1] is the last element we've decoded (equivalent to arr[i])
    • x is the current encoded value (equivalent to encoded[i])
    • The XOR operation ^ gives us the next element
  4. Build the array incrementally: Append each newly decoded element to our result array.

The implementation is straightforward:

ans = [first]  # Start with arr[0]
for x in encoded:
    ans.append(ans[-1] ^ x)  # arr[i+1] = arr[i] XOR encoded[i]
return ans

This approach has:

  • Time Complexity: O(n) where n is the length of the encoded array, as we iterate through it once
  • Space Complexity: O(n) for storing the decoded array (which is the required output)

The solution leverages the self-inverse property of XOR to reverse the encoding process, reconstructing the original array element by element from left to right.

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

Let's walk through a concrete example to see how the solution works.

Given:

  • encoded = [3, 1, 4, 2]
  • first = 5

We need to reconstruct the original array arr.

Step-by-step reconstruction:

  1. Initialize: Start with arr = [5] (since first = 5)

  2. Decode arr[1]:

    • We know: encoded[0] = arr[0] XOR arr[1] = 3
    • Therefore: arr[1] = arr[0] XOR encoded[0] = 5 XOR 3
    • Binary: 5 = 101, 3 = 011
    • XOR: 101 XOR 011 = 110 = 6
    • Update: arr = [5, 6]
  3. Decode arr[2]:

    • We know: encoded[1] = arr[1] XOR arr[2] = 1
    • Therefore: arr[2] = arr[1] XOR encoded[1] = 6 XOR 1
    • Binary: 6 = 110, 1 = 001
    • XOR: 110 XOR 001 = 111 = 7
    • Update: arr = [5, 6, 7]
  4. Decode arr[3]:

    • We know: encoded[2] = arr[2] XOR arr[3] = 4
    • Therefore: arr[3] = arr[2] XOR encoded[2] = 7 XOR 4
    • Binary: 7 = 111, 4 = 100
    • XOR: 111 XOR 100 = 011 = 3
    • Update: arr = [5, 6, 7, 3]
  5. Decode arr[4]:

    • We know: encoded[3] = arr[3] XOR arr[4] = 2
    • Therefore: arr[4] = arr[3] XOR encoded[3] = 3 XOR 2
    • Binary: 3 = 011, 2 = 010
    • XOR: 011 XOR 010 = 001 = 1
    • Final: arr = [5, 6, 7, 3, 1]

Verification: Let's verify our answer by encoding it back:

  • 5 XOR 6 = 101 XOR 110 = 011 = 3
  • 6 XOR 7 = 110 XOR 111 = 001 = 1
  • 7 XOR 3 = 111 XOR 011 = 100 = 4
  • 3 XOR 1 = 011 XOR 001 = 010 = 2

The decoded array [5, 6, 7, 3, 1] correctly produces the encoded array [3, 1, 4, 2] when we apply the XOR operation to consecutive elements.

Solution Implementation

1class Solution:
2    def decode(self, encoded: List[int], first: int) -> List[int]:
3        """
4        Decodes an array where each element in 'encoded' represents 
5        the XOR of consecutive elements in the original array.
6      
7        Args:
8            encoded: List of integers where encoded[i] = original[i] XOR original[i+1]
9            first: The first element of the original array
10          
11        Returns:
12            The original array before encoding
13        """
14        # Initialize result array with the first element
15        result = [first]
16      
17        # Iterate through each encoded value
18        for encoded_value in encoded:
19            # Calculate next original value using XOR property:
20            # If encoded[i] = original[i] XOR original[i+1]
21            # Then original[i+1] = original[i] XOR encoded[i]
22            next_value = result[-1] ^ encoded_value
23            result.append(next_value)
24      
25        return result
26
1class Solution {
2    /**
3     * Decodes an XOR-encoded array given the first element of the original array.
4     * 
5     * The encoding rule is: encoded[i] = original[i] XOR original[i + 1]
6     * Therefore, to decode: original[i + 1] = original[i] XOR encoded[i]
7     * 
8     * @param encoded The XOR-encoded array
9     * @param first The first element of the original array
10     * @return The decoded original array
11     */
12    public int[] decode(int[] encoded, int first) {
13        // Get the length of the encoded array
14        int encodedLength = encoded.length;
15      
16        // Initialize result array with size encodedLength + 1
17        // (since original array has one more element than encoded array)
18        int[] decodedArray = new int[encodedLength + 1];
19      
20        // Set the first element of the decoded array
21        decodedArray[0] = first;
22      
23        // Decode each subsequent element using XOR operation
24        // Since encoded[i] = original[i] XOR original[i + 1],
25        // we can get original[i + 1] = original[i] XOR encoded[i]
26        for (int i = 0; i < encodedLength; i++) {
27            decodedArray[i + 1] = decodedArray[i] ^ encoded[i];
28        }
29      
30        // Return the fully decoded array
31        return decodedArray;
32    }
33}
34
1class Solution {
2public:
3    vector<int> decode(vector<int>& encoded, int first) {
4        // Initialize result array with the first element
5        vector<int> result = {first};
6      
7        // Decode each element by XORing with the previous decoded value
8        // Since encoded[i] = arr[i] XOR arr[i+1], we have:
9        // arr[i+1] = encoded[i] XOR arr[i]
10        for (int encodedValue : encoded) {
11            // Get the last decoded value and XOR with current encoded value
12            int nextDecodedValue = result.back() ^ encodedValue;
13            result.push_back(nextDecodedValue);
14        }
15      
16        return result;
17    }
18};
19
1/**
2 * Decodes an array of XOR-encoded numbers given the first element of the original array.
3 * Each encoded element represents the XOR of consecutive elements in the original array.
4 * 
5 * @param encoded - Array of XOR-encoded values where encoded[i] = original[i] ^ original[i+1]
6 * @param first - The first element of the original array
7 * @returns The decoded original array
8 */
9function decode(encoded: number[], first: number): number[] {
10    // Initialize result array with the first element
11    const decodedArray: number[] = [first];
12  
13    // Iterate through each encoded value
14    for (const encodedValue of encoded) {
15        // Decode the next element by XORing the last decoded element with the current encoded value
16        // Since encoded[i] = original[i] ^ original[i+1], we can get original[i+1] = original[i] ^ encoded[i]
17        const lastDecodedElement: number = decodedArray[decodedArray.length - 1];
18        const nextDecodedElement: number = lastDecodedElement ^ encodedValue;
19        decodedArray.push(nextDecodedElement);
20    }
21  
22    return decodedArray;
23}
24

Time and Space Complexity

Time Complexity: O(n)

The algorithm iterates through the encoded list exactly once using a for loop. In each iteration, it performs a constant-time XOR operation (ans[-1] ^ x) and appends the result to the ans list. Since appending to a list in Python is amortized O(1) and we perform n iterations where n is the length of the encoded array, the overall time complexity is O(n).

Space Complexity: O(n)

The algorithm creates a new list ans to store the decoded result. This list starts with one element (first) and grows by one element for each element in the encoded array. The final size of ans will be n + 1 where n is the length of the encoded array. Therefore, the space complexity is O(n) for storing the output array. The only additional space used is for the loop variable x, which is O(1), so the overall space complexity remains O(n).

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

Common Pitfalls

1. Misunderstanding XOR Properties

Many developers struggle with the XOR operation's reversibility. A common mistake is trying to "undo" XOR using subtraction or other operations instead of applying XOR again.

Incorrect approach:

# Wrong: Trying to reverse XOR with subtraction
result.append(encoded_value - result[-1])

Correct approach:

# Right: XOR is its own inverse
result.append(result[-1] ^ encoded_value)

2. Off-by-One Errors with Array Lengths

The encoded array has length n-1 while the original array has length n. Developers often create arrays of incorrect size or loop the wrong number of times.

Incorrect approach:

# Wrong: Pre-allocating wrong size
result = [0] * len(encoded)  # Missing one element!
result[0] = first
for i in range(1, len(encoded)):
    result[i] = result[i-1] ^ encoded[i-1]
# This will miss the last element

Correct approach:

# Right: Account for n vs n-1 relationship
result = [first]
for encoded_value in encoded:
    result.append(result[-1] ^ encoded_value)
# Final array will have len(encoded) + 1 elements

3. Modifying Input While Iterating

Some developers try to modify the encoded array in place or use it to store intermediate results, which can lead to incorrect calculations.

Incorrect approach:

# Wrong: Modifying encoded array
encoded.insert(0, first)
for i in range(1, len(encoded)):
    encoded[i] = encoded[i-1] ^ encoded[i]
return encoded  # This corrupts the input and gives wrong results

Correct approach:

# Right: Create a new array for results
result = [first]
for encoded_value in encoded:
    result.append(result[-1] ^ encoded_value)
return result

4. Incorrect Index Management

When using index-based iteration, it's easy to confuse which indices correspond to the encoded array versus the original array.

Incorrect approach:

# Wrong: Confusing indices between arrays
result = [first]
for i in range(len(encoded)):
    result.append(result[i+1] ^ encoded[i])  # IndexError!

Correct approach:

# Right: Careful index management
result = [first]
for i in range(len(encoded)):
    result.append(result[i] ^ encoded[i])

5. Not Handling Edge Cases

Failing to consider empty encoded arrays or validate inputs can cause runtime errors.

Better approach with validation:

def decode(self, encoded: List[int], first: int) -> List[int]:
    if not encoded:  # Handle empty encoded array
        return [first]
  
    result = [first]
    for encoded_value in encoded:
        result.append(result[-1] ^ encoded_value)
  
    return result
Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

Depth first search is equivalent to which of the tree traversal order?


Recommended Readings

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

Load More