Loading problems...
TL;DR: Use a two-pointer approach to verify that the sequence of 'L' and 'R' characters matches in both strings and that their indices satisfy the directional movement constraints (L moves left, R moves right).
In the LeetCode 2337 problem, "Move Pieces to Obtain a String", you are given two strings, start and target, consisting of 'L', 'R', and '_' (underscores representing empty spaces). The rules define that 'L' pieces can move to the left into adjacent empty spaces, and 'R' pieces can move to the right into adjacent empty spaces. The pieces cannot jump over each other. The goal is to determine if start can be transformed into target through any sequence of valid moves.
This is a popular interview question because it tests the ability to identify invariants in a simulation problem without actually performing the simulation.
A naive brute force approach would attempt to simulate every possible valid move from the start state to see if the target state is reachable. This is typically modeled as a Breadth-First Search (BFS) or Depth-First Search (DFS) on the state space of the string.
Pseudo-code:
textfunction canChange(start, target): queue = [start] visited = {start} while queue is not empty: current = queue.pop() if current == target: return true for every possible move in current: next_state = perform_move(current) if next_state not in visited: visited.add(next_state) queue.push(next_state) return false
Complexity Analysis:
visited set.Why it fails: The constraints specify . A BFS approach will immediately hit a Time Limit Exceeded (TLE) or Memory Limit Exceeded (MLE) error because the state space is too large to traverse. We need a linear time solution that checks validity without simulation.
The key intuition for the optimal LeetCode 2337 solution relies on two fundamental physical constraints enforced by the problem rules:
start and target, the resulting strings must be identical. For example, if start has the sequence "L...R" and target has "R...L", transformation is impossible.start must be greater than or equal to its index in target (index_start >= index_target). If the 'L' starts at index 3 and needs to be at index 5, it's impossible because 'L' cannot move right.start must be less than or equal to its index in target (index_start <= index_target).Visual Description:
Imagine two pointers, one for each string. As they traverse from left to right, they skip all underscores. When both pointers land on a character ('L' or 'R'), those characters must match. Furthermore, based on the character type, we check the indices. If start has an 'L' at index 5, and the corresponding 'L' in target is at index 2, the move is valid (5 -> 2 is left). If the target 'L' was at index 7, it would be invalid.

Consider start = "_L__R__R_", target = "L______RR".
i=0, j=0.i moves to 1 (start[1] = 'L').j stays at 0 (target[0] = 'L').i >= j. is True.i becomes 2, j becomes 1.i moves to 4 (start[4] = 'R').j moves to 7 (target[7] = 'R').i <= j. is True.i becomes 5, j becomes 8.i moves to 7 (start[7] = 'R').j moves to 8 (target[8] = 'R').i <= j. is True.true.The correctness rests on the fact that the conditions checked are both necessary and sufficient.
cppclass Solution { public: bool canChange(string start, string target) { int n = start.length(); int i = 0, j = 0; while (i < n || j < n) { // Skip underscores in start while (i < n && start[i] == '_') { i++; } // Skip underscores in target while (j < n && target[j] == '_') { j++; } // If one reaches end and other doesn't, count mismatch if (i == n || j == n) { return i == n && j == n; } // Check if pieces match if (start[i] != target[j]) { return false; } // Check directional constraints if (start[i] == 'L' && i < j) { return false; // 'L' cannot move right } if (start[i] == 'R' && i > j) { return false; // 'R' cannot move left } // Move to next potential piece i++; j++; } return true; } };
javaclass Solution { public boolean canChange(String start, String target) { int n = start.length(); int i = 0; int j = 0; while (i < n || j < n) { // Skip underscores in start while (i < n && start.charAt(i) == '_') { i++; } // Skip underscores in target while (j < n && target.charAt(j) == '_') { j++; } // If one string is exhausted, both must be exhausted if (i == n || j == n) { return i == n && j == n; } // Check if the current pieces are different if (start.charAt(i) != target.charAt(j)) { return false; } // Check constraints for 'L' (can only move left) if (start.charAt(i) == 'L' && i < j) { return false; } // Check constraints for 'R' (can only move right) if (start.charAt(i) == 'R' && i > j) { return false; } i++; j++; } return true; } }
pythonclass Solution: def canChange(self, start: str, target: str) -> bool: n = len(start) i, j = 0, 0 while i < n or j < n: # Skip underscores in start while i < n and start[i] == '_': i += 1 # Skip underscores in target while j < n and target[j] == '_': j += 1 # If one pointer reaches the end, both must reach the end if i == n or j == n: return i == n and j == n # Check if pieces match if start[i] != target[j]: return false # Check 'L' constraint: cannot move right (i must be >= j) if start[i] == 'L' and i < j: return False # Check 'R' constraint: cannot move left (i must be <= j) if start[i] == 'R' and i > j: return False # Move to next i += 1 j += 1 return True
while loops for skipping underscores do not increase the overall complexity because i and j only move forward.i and j). No auxiliary data structures (like arrays or hash maps) are used.The Two Pointer pattern used here is fundamental for array and string manipulation. It appears in various forms:
In all these cases, the core strategy involves traversing the data linearly to enforce a specific structure or invariant without using extra space.
Why does the naive BFS solution TLE?
Why does my solution fail on start="_L" and target="LL"?
Why is checking start.replace('_', '') == target.replace('_', '') not enough?
true for cases like start="L_" and target="_L", which is physically impossible.