777. Swap Adjacent in LR String
Problem Description
You are given two strings start
and end
that consist only of the characters 'L'
, 'R'
, and 'X'
. You need to determine if you can transform the start
string into the end
string using a sequence of valid moves.
The valid moves are:
- Replace
"XL"
with"LX"
(moving anL
to the left) - Replace
"RX"
with"XR"
(moving anR
to the right)
For example, given start = "RXXLRXRXL"
, you can apply moves to potentially transform it into another valid configuration.
The key insight is that:
L
characters can only move left (by swapping withX
on their left)R
characters can only move right (by swapping withX
on their right)- The relative order of
L
andR
characters cannot change X
characters act as empty spaces that allowL
andR
to move
The solution checks if transformation is possible by:
- Skipping all
X
characters in both strings to compare only theL
andR
positions - Ensuring the sequence of
L
andR
characters is identical in both strings - Verifying that each
L
instart
is at a position greater than or equal to its corresponding position inend
(sinceL
can only move left) - Verifying that each
R
instart
is at a position less than or equal to its corresponding position inend
(sinceR
can only move right)
The function returns True
if the transformation is possible, False
otherwise.
Intuition
The first observation is understanding what these moves actually do. When we replace "XL"
with "LX"
, we're essentially moving an L
one position to the left. Similarly, replacing "RX"
with "XR"
moves an R
one position to the right. The X
characters act like empty spaces that allow movement.
Think of it like a sliding puzzle where L
pieces can only slide left, R
pieces can only slide right, and X
represents empty slots. This immediately tells us something crucial: the relative order of L
and R
characters can never change! An L
can never move past an R
, and an R
can never move past an L
, because they move in opposite directions.
This leads to our first key insight: if we remove all X
characters from both strings, the resulting sequences of L
and R
must be identical. If they're not, transformation is impossible.
The second insight comes from the movement constraints:
- Since
L
can only move left, if anL
appears at positioni
instart
and needs to be at positionj
inend
, theni >= j
must be true - Since
R
can only move right, if anR
appears at positioni
instart
and needs to be at positionj
inend
, theni <= j
must be true
This naturally leads to a two-pointer approach: we can iterate through both strings simultaneously, skipping X
characters, and check:
- Do the non-
X
characters match in order? - Can each
L
reach its target position (by moving left)? - Can each
R
reach its target position (by moving right)?
If all these conditions are met, the transformation is possible. The beauty of this solution is that we don't need to simulate the actual moves - we just need to verify that the constraints allow the transformation.
Learn more about Two Pointers patterns.
Solution Approach
The solution uses a two-pointer technique to validate if the transformation is possible. Here's how the implementation works:
We initialize two pointers i
and j
to traverse through start
and end
strings respectively. The algorithm enters an infinite loop that continues until we determine whether the transformation is valid.
Step 1: Skip X characters
while i < n and start[i] == 'X': i += 1 while j < n and end[j] == 'X': j += 1
We advance both pointers to skip all X
characters since they don't affect the validity of the transformation - they merely act as spaces for movement.
Step 2: Check termination conditions
if i >= n and j >= n: return True
If both pointers have reached the end of their respective strings, we've successfully matched all non-X
characters, so the transformation is valid.
Step 3: Validate character matching
if i >= n or j >= n or start[i] != end[j]: return False
If only one pointer has reached the end, or if the current non-X
characters don't match, the transformation is impossible. This ensures the sequence of L
and R
characters remains the same.
Step 4: Check movement constraints
if start[i] == 'L' and i < j: return False if start[i] == 'R' and i > j: return False
- For an
L
character: ifi < j
, it means theL
needs to move right to reach its target position, which is impossible sinceL
can only move left - For an
R
character: ifi > j
, it means theR
needs to move left to reach its target position, which is impossible sinceR
can only move right
Step 5: Advance pointers
i, j = i + 1, j + 1
After validating the current pair of non-X
characters, we move both pointers forward to check the next pair.
The algorithm has a time complexity of O(n)
where n
is the length of the strings, as we traverse each string at most once. The space complexity is O(1)
as we only use a constant amount of extra space for the pointers.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach.
Example: start = "RXXLRXRXL"
and end = "XRLXRXRLX"
We'll use two pointers i
and j
starting at position 0 for both strings.
Iteration 1:
i = 0
:start[0] = 'R'
(not X, so we stop)j = 0
:end[0] = 'X'
β advance toj = 1
:end[1] = 'R'
(not X, so we stop)- Compare:
start[0] = 'R'
andend[1] = 'R'
β (characters match) - Check movement:
R
at position 0 needs to move to position 1. SinceR
can move right and0 < 1
, this is valid β - Advance:
i = 1, j = 2
Iteration 2:
i = 1
:start[1] = 'X'
β advance toi = 2
:start[2] = 'X'
β advance toi = 3
:start[3] = 'L'
j = 2
:end[2] = 'L'
(not X, so we stop)- Compare:
start[3] = 'L'
andend[2] = 'L'
β (characters match) - Check movement:
L
at position 3 needs to move to position 2. SinceL
can move left and3 > 2
, this is valid β - Advance:
i = 4, j = 3
Iteration 3:
i = 4
:start[4] = 'R'
(not X, so we stop)j = 3
:end[3] = 'X'
β advance toj = 4
:end[4] = 'R'
(not X, so we stop)- Compare:
start[4] = 'R'
andend[4] = 'R'
β (characters match) - Check movement:
R
at position 4 needs to move to position 4. Since positions are equal, no movement needed β - Advance:
i = 5, j = 5
Iteration 4:
i = 5
:start[5] = 'X'
β advance toi = 6
:start[6] = 'R'
j = 5
:end[5] = 'X'
β advance toj = 6
:end[6] = 'R'
- Compare:
start[6] = 'R'
andend[6] = 'R'
β (characters match) - Check movement:
R
at position 6 needs to move to position 6. No movement needed β - Advance:
i = 7, j = 7
Iteration 5:
i = 7
:start[7] = 'X'
β advance toi = 8
:start[8] = 'L'
j = 7
:end[7] = 'L'
(not X, so we stop)- Compare:
start[8] = 'L'
andend[7] = 'L'
β (characters match) - Check movement:
L
at position 8 needs to move to position 7. SinceL
can move left and8 > 7
, this is valid β - Advance:
i = 9, j = 8
Iteration 6:
i = 9
: We've reached the end ofstart
(i >= n)j = 8
:end[8] = 'X'
β advance toj = 9
: We've reached the end ofend
(j >= n)- Both pointers have reached the end, so return
True
β
The transformation is possible! Each L
moved left (or stayed in place), each R
moved right (or stayed in place), and the relative order of all L
and R
characters was preserved.
Solution Implementation
1class Solution:
2 def canTransform(self, start: str, end: str) -> bool:
3 """
4 Determine if we can transform 'start' string to 'end' string.
5 Rules: 'L' can only move left (bypassing 'X'), 'R' can only move right (bypassing 'X')
6
7 Args:
8 start: Source string containing 'L', 'R', and 'X' characters
9 end: Target string containing 'L', 'R', and 'X' characters
10
11 Returns:
12 True if transformation is possible, False otherwise
13 """
14 n = len(start)
15 i = 0 # Pointer for start string
16 j = 0 # Pointer for end string
17
18 while True:
19 # Skip all 'X' characters in start string
20 while i < n and start[i] == 'X':
21 i += 1
22
23 # Skip all 'X' characters in end string
24 while j < n and end[j] == 'X':
25 j += 1
26
27 # Both pointers reached the end - transformation successful
28 if i >= n and j >= n:
29 return True
30
31 # One pointer reached the end but not the other - transformation impossible
32 # Or the non-X characters don't match
33 if i >= n or j >= n or start[i] != end[j]:
34 return False
35
36 # 'L' can only move left, so its position in start must be >= its position in end
37 if start[i] == 'L' and i < j:
38 return False
39
40 # 'R' can only move right, so its position in start must be <= its position in end
41 if start[i] == 'R' and i > j:
42 return False
43
44 # Move both pointers forward
45 i += 1
46 j += 1
47
1class Solution {
2 public boolean canTransform(String start, String end) {
3 int n = start.length();
4 int startIndex = 0;
5 int endIndex = 0;
6
7 while (true) {
8 // Skip all 'X' characters in the start string
9 while (startIndex < n && start.charAt(startIndex) == 'X') {
10 startIndex++;
11 }
12
13 // Skip all 'X' characters in the end string
14 while (endIndex < n && end.charAt(endIndex) == 'X') {
15 endIndex++;
16 }
17
18 // If both pointers reached the end, transformation is possible
19 if (startIndex == n && endIndex == n) {
20 return true;
21 }
22
23 // If only one pointer reached the end, or characters don't match
24 if (startIndex == n || endIndex == n ||
25 start.charAt(startIndex) != end.charAt(endIndex)) {
26 return false;
27 }
28
29 // Check movement constraints:
30 // 'L' can only move left (startIndex should be >= endIndex)
31 // 'R' can only move right (startIndex should be <= endIndex)
32 if ((start.charAt(startIndex) == 'L' && startIndex < endIndex) ||
33 (start.charAt(startIndex) == 'R' && startIndex > endIndex)) {
34 return false;
35 }
36
37 // Move both pointers forward
38 startIndex++;
39 endIndex++;
40 }
41 }
42}
43
1class Solution {
2public:
3 bool canTransform(string start, string end) {
4 int n = start.size();
5 int startIndex = 0;
6 int endIndex = 0;
7
8 while (true) {
9 // Skip all 'X' characters in the start string
10 while (startIndex < n && start[startIndex] == 'X') {
11 startIndex++;
12 }
13
14 // Skip all 'X' characters in the end string
15 while (endIndex < n && end[endIndex] == 'X') {
16 endIndex++;
17 }
18
19 // If both pointers reached the end, transformation is possible
20 if (startIndex == n && endIndex == n) {
21 return true;
22 }
23
24 // If only one pointer reached the end, or characters don't match
25 if (startIndex == n || endIndex == n || start[startIndex] != end[endIndex]) {
26 return false;
27 }
28
29 // 'L' can only move left, so its position in start must be >= its position in end
30 if (start[startIndex] == 'L' && startIndex < endIndex) {
31 return false;
32 }
33
34 // 'R' can only move right, so its position in start must be <= its position in end
35 if (start[startIndex] == 'R' && startIndex > endIndex) {
36 return false;
37 }
38
39 // Move to the next non-'X' character
40 startIndex++;
41 endIndex++;
42 }
43 }
44};
45
1function canTransform(start: string, end: string): boolean {
2 const n: number = start.length;
3 let startIndex: number = 0;
4 let endIndex: number = 0;
5
6 while (true) {
7 // Skip all 'X' characters in the start string
8 while (startIndex < n && start[startIndex] === 'X') {
9 startIndex++;
10 }
11
12 // Skip all 'X' characters in the end string
13 while (endIndex < n && end[endIndex] === 'X') {
14 endIndex++;
15 }
16
17 // If both pointers reached the end, transformation is possible
18 if (startIndex === n && endIndex === n) {
19 return true;
20 }
21
22 // If only one pointer reached the end, or characters don't match
23 if (startIndex === n || endIndex === n || start[startIndex] !== end[endIndex]) {
24 return false;
25 }
26
27 // 'L' can only move left, so its position in start must be >= its position in end
28 if (start[startIndex] === 'L' && startIndex < endIndex) {
29 return false;
30 }
31
32 // 'R' can only move right, so its position in start must be <= its position in end
33 if (start[startIndex] === 'R' && startIndex > endIndex) {
34 return false;
35 }
36
37 // Move to the next non-'X' character
38 startIndex++;
39 endIndex++;
40 }
41}
42
Time and Space Complexity
Time Complexity: O(n)
, where n
is the length of the input strings.
The algorithm uses two pointers i
and j
that traverse through the start
and end
strings respectively. In each iteration of the main loop, at least one of the pointers advances. Since both pointers can move at most n
positions (the length of the strings), and each pointer movement involves constant-time operations, the total time complexity is linear. Specifically:
- The inner while loops skip 'X' characters, but each character is visited at most once
- The comparison and validation operations are all
O(1)
- Both pointers move from index 0 to at most index
n
Space Complexity: O(1)
The algorithm only uses a constant amount of extra space:
- Two integer pointers
i
andj
to track positions in the strings - The variable
n
to store the length - No additional data structures are created
- The space used doesn't scale with the input size
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Forgetting to Check String Lengths
Pitfall: Not verifying that both input strings have the same length before processing. If the strings have different lengths, transformation is impossible, but the algorithm might not handle this gracefully.
Solution: Add a length check at the beginning:
if len(start) != len(end):
return False
2. Incorrect Boundary Check Order
Pitfall: Checking start[i]
or end[j]
before verifying that i < n
or j < n
. This can cause an IndexError when trying to access characters beyond the string length.
Wrong approach:
# Dangerous - might access out of bounds if start[i] != end[j] or i >= n or j >= n: return False
Solution: Always check boundaries before accessing array elements:
# Correct order - check bounds first if i >= n or j >= n or start[i] != end[j]: return False
3. Missing the Case Where Character Counts Don't Match
Pitfall: The current implementation correctly handles this through the two-pointer approach, but a common mistake when implementing this algorithm is to only check positions without verifying that the count of 'L' and 'R' characters matches. For example, "XL" and "RX" both have one non-X character, but they're obviously not transformable.
Solution: The two-pointer approach inherently handles this by ensuring characters match at each step (start[i] != end[j]
check), but if implementing differently, explicitly verify:
# Alternative validation approach if start.count('L') != end.count('L') or start.count('R') != end.count('R'): return False
4. Confusing Movement Direction Logic
Pitfall: Mixing up the movement constraints - remembering that 'L' moves left and 'R' moves right is intuitive, but translating this to index comparisons can be confusing. A common mistake is reversing the inequality checks.
Wrong logic:
# Incorrect - reversed logic if start[i] == 'L' and i > j: # Wrong! L at position i needs to reach position j return False
Solution: Remember the index relationship:
- If 'L' needs to move from index
i
to indexj
, and 'L' can only move left, theni
must be β₯j
- If 'R' needs to move from index
i
to indexj
, and 'R' can only move right, theni
must be β€j
5. Not Handling Edge Cases with All X's
Pitfall: Special cases like strings containing only 'X' characters or empty strings might not be handled correctly if the loop termination conditions aren't properly set.
Solution: The current implementation handles this well with the condition if i >= n and j >= n: return True
, which correctly returns True for matching all-X strings or empty strings. However, ensure this check comes before attempting to access string characters.
Which of the following shows the order of node visit in a Breadth-first Search?
Recommended Readings
Tech Interview Pattern Two Pointers Introduction If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe src https www youtube com embed xZ4AfXHQ1VQ title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture allowfullscreen iframe div Two pointers is a common interview
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 assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Donβt Miss This!