Facebook Pixel

1460. Make Two Arrays Equal by Reversing Subarrays

Problem Description

You have two integer arrays target and arr that have the same length. Your goal is to transform arr to make it exactly equal to target.

The only operation you can perform is:

  • Select any non-empty subarray from arr and reverse it

You can perform this reversal operation as many times as you want (including zero times).

Return true if it's possible to make arr equal to target through these operations, or false if it's impossible.

Example understanding:

  • If arr = [1, 2, 3, 4] and you reverse the subarray from index 1 to 3, you get [1, 4, 3, 2]
  • A subarray can be as small as a single element or as large as the entire array
  • The operation can be repeated multiple times on different (or same) subarrays

The key insight is that if both arrays contain the same elements with the same frequencies, we can always rearrange one to match the other through a series of reversals. This is because reversing subarrays allows us to move any element to any position through a sequence of operations. Therefore, checking if the sorted versions of both arrays are equal tells us whether transformation is possible.

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

Intuition

Let's think about what the reversal operation actually allows us to do. When we reverse a subarray, we're essentially rearranging elements within that portion of the array.

Consider what happens with repeated reversals:

  • Reversing a subarray of size 2 swaps two adjacent elements
  • By carefully choosing which subarrays to reverse, we can move any element to any position

For example, to move an element from position i to position j:

  • We can use a series of adjacent swaps (reversing subarrays of size 2)
  • This is similar to bubble sort, where we can bubble any element to any position

Since we can perform unlimited reversals and move any element anywhere, the question becomes: Do both arrays contain the exact same elements with the same frequencies?

If target has two 3's, one 5, and three 7's, then arr must also have exactly two 3's, one 5, and three 7's for transformation to be possible. The actual positions don't matter because we can rearrange them however we want through reversals.

This realization leads us to a simple check: if both arrays contain the same elements with the same counts, they can be made equal. The easiest way to verify this is to sort both arrays and compare them. If sorted(target) == sorted(arr), then:

  • They have the same elements
  • Each element appears the same number of times in both arrays
  • Therefore, we can transform one into the other through reversals

Learn more about Sorting patterns.

Solution Approach

The solution implements the sorting-based approach we identified from our intuition. Here's how it works:

Algorithm Steps:

  1. Sort both the target array and the arr array
  2. Compare the sorted arrays for equality
  3. Return true if they're equal, false otherwise

Implementation Details:

The Python solution is remarkably concise:

return sorted(target) == sorted(arr)

This single line accomplishes everything we need:

  • sorted(target) creates a new sorted list from the target array
  • sorted(arr) creates a new sorted list from the arr array
  • The == operator compares these two sorted lists element by element
  • If all elements match at their respective positions, it returns true; otherwise false

Why This Works:

When we sort both arrays:

  • Elements with the same value are grouped together
  • If both arrays have the same elements with the same frequencies, their sorted versions will be identical
  • For example:
    • target = [3, 7, 1, 3] becomes [1, 3, 3, 7] after sorting
    • arr = [1, 3, 7, 3] becomes [1, 3, 3, 7] after sorting
    • Since the sorted arrays are equal, we can transform one into the other

Time and Space Complexity:

  • Time Complexity: O(n log n) where n is the length of the arrays, due to the sorting operation
  • Space Complexity: O(n) for creating the sorted copies of the arrays

This approach elegantly reduces the problem from finding a sequence of reversals to simply checking if two arrays contain the same multiset of elements.

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

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

Example 1: Arrays that can be made equal

Given:

  • target = [3, 7, 9, 3]
  • arr = [3, 9, 3, 7]

Step 1: Sort both arrays

  • sorted(target) = [3, 3, 7, 9]
  • sorted(arr) = [3, 3, 7, 9]

Step 2: Compare sorted arrays

  • Both sorted arrays are [3, 3, 7, 9]
  • They are equal, so we return true

Verification through reversals: To confirm this works, let's actually transform arr to target:

  • Start: arr = [3, 9, 3, 7], want target = [3, 7, 9, 3]
  • Reverse subarray from index 1 to 2: [3, 3, 9, 7]
  • Reverse subarray from index 1 to 3: [3, 7, 9, 3]
  • We've successfully transformed arr into target!

Example 2: Arrays that cannot be made equal

Given:

  • target = [1, 2, 2, 3]
  • arr = [1, 1, 2, 3]

Step 1: Sort both arrays

  • sorted(target) = [1, 2, 2, 3]
  • sorted(arr) = [1, 1, 2, 3]

Step 2: Compare sorted arrays

  • sorted(target) has two 2's at positions 1 and 2
  • sorted(arr) has two 1's at positions 0 and 1
  • The sorted arrays are different, so we return false

Why reversals can't help: No matter how many reversals we perform on arr, we can't create a second 2 or eliminate a 1. Reversals only rearrange existing elements; they don't change what elements we have or their frequencies.

Solution Implementation

1class Solution:
2    def canBeEqual(self, target: List[int], arr: List[int]) -> bool:
3        """
4        Determine if two arrays can be made equal through any number of operations.
5      
6        The operation allowed is: reverse any subarray of arr.
7        Since we can reverse any subarray, we can effectively perform any permutation.
8        Therefore, two arrays can be made equal if they contain the same elements
9        with the same frequencies.
10      
11        Args:
12            target: The target array to match
13            arr: The array to transform through operations
14          
15        Returns:
16            True if arr can be transformed to equal target, False otherwise
17        """
18        # Sort both arrays and compare - if they have the same elements
19        # with same frequencies, their sorted versions will be identical
20        return sorted(target) == sorted(arr)
21
1class Solution {
2    /**
3     * Determines if two arrays can be made equal by rearranging elements.
4     * 
5     * The approach sorts both arrays and checks if they contain the same elements
6     * with the same frequencies. If the sorted arrays are equal, then one can be
7     * rearranged to match the other.
8     * 
9     * @param target The target array to match
10     * @param arr The array to be rearranged
11     * @return true if arr can be rearranged to equal target, false otherwise
12     */
13    public boolean canBeEqual(int[] target, int[] arr) {
14        // Sort both arrays to compare their elements
15        Arrays.sort(target);
16        Arrays.sort(arr);
17      
18        // Check if the sorted arrays are identical
19        // If they are, arr can be rearranged to match target
20        return Arrays.equals(target, arr);
21    }
22}
23
1class Solution {
2public:
3    /**
4     * Determines if two arrays can be made equal by reordering elements.
5     * 
6     * @param target The target array to compare against
7     * @param arr The array to check if it can be rearranged to match target
8     * @return true if arr can be rearranged to equal target, false otherwise
9     */
10    bool canBeEqual(vector<int>& target, vector<int>& arr) {
11        // Sort both arrays in ascending order
12        sort(target.begin(), target.end());
13        sort(arr.begin(), arr.end());
14      
15        // After sorting, if arrays have the same elements with same frequencies,
16        // they will be identical
17        return target == arr;
18    }
19};
20
1/**
2 * Determines if two arrays can be made equal by reordering elements
3 * @param target - The target array to compare against
4 * @param arr - The array to check if it can be reordered to match target
5 * @returns true if arrays contain the same elements with same frequencies, false otherwise
6 */
7function canBeEqual(target: number[], arr: number[]): boolean {
8    // Sort both arrays in ascending order
9    target.sort((a, b) => a - b);
10    arr.sort((a, b) => a - b);
11  
12    // Check if every element at each index matches after sorting
13    // If all elements match, the arrays contain the same elements with same frequencies
14    return target.every((element, index) => element === arr[index]);
15}
16

Time and Space Complexity

The time complexity is O(n Γ— log n), where n is the length of the arrays. This is because the sorted() function in Python uses Timsort algorithm, which has a time complexity of O(n Γ— log n) for sorting each array. Since we sort both target and arr, we perform two sorting operations, but this still results in O(n Γ— log n) overall complexity.

The space complexity is O(n) for the general case. The sorted() function creates new sorted lists for both target and arr, requiring O(n) space for each sorted array. However, the reference answer states O(log n) space complexity, which would be accurate if we consider only the auxiliary space used by the sorting algorithm itself (the recursion stack depth in Timsort), excluding the space for the output sorted arrays. In the context of this specific problem where we're creating new sorted arrays, the total space used is O(n).

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

Common Pitfalls

1. Attempting to Track Actual Reversal Operations

A common mistake is trying to find the actual sequence of reversals needed to transform arr into target. Developers might attempt complex algorithms involving:

  • Tracking positions and movements of elements
  • Implementing actual reversal operations
  • Using BFS/DFS to explore different reversal sequences

Why it's problematic: This approach is unnecessarily complex and can lead to exponential time complexity as there are many possible sequences of operations.

Solution: Recognize that we only need to determine if transformation is possible, not find the actual steps. The sorting approach elegantly sidesteps this complexity.

2. Using a Hash Map/Counter Without Proper Comparison

Some might use a frequency counter but implement the comparison incorrectly:

# Incorrect approach - only checks keys, not frequencies
def canBeEqual(self, target, arr):
    return set(target) == set(arr)  # Wrong! Ignores duplicates

Why it's problematic: Using sets removes duplicate values, so [1, 1, 2] and [1, 2, 2] would incorrectly return true.

Solution: If using a frequency-based approach, properly count occurrences:

from collections import Counter
def canBeEqual(self, target, arr):
    return Counter(target) == Counter(arr)

3. Modifying Input Arrays In-Place

Attempting to sort arrays in-place to save space:

# Problematic if arrays shouldn't be modified
def canBeEqual(self, target, arr):
    target.sort()  # Modifies original array
    arr.sort()     # Modifies original array
    return target == arr

Why it's problematic: This modifies the input arrays, which might be unexpected behavior for the caller and could cause issues if the arrays are used elsewhere.

Solution: Use sorted() which returns new sorted lists without modifying the originals, or explicitly copy arrays first if in-place sorting is needed for space optimization.

4. Overlooking Edge Cases with Empty Arrays

Not considering whether empty arrays or single-element arrays need special handling.

Why it's problematic: Developers might add unnecessary special case handling:

def canBeEqual(self, target, arr):
    if len(target) == 0 or len(arr) == 0:
        return len(target) == len(arr)  # Unnecessary
    return sorted(target) == sorted(arr)

Solution: The sorting approach naturally handles all edge cases including empty arrays and single elements. No special handling is needed.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the two traversal algorithms (BFS and DFS) can be used to find whether two nodes are connected?


Recommended Readings

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

Load More