777. Swap Adjacent in LR String

In a string composed of 'L', 'R', and 'X' characters, like "RXXLRXRXL", a move consists of either replacing one occurrence of "XL" with "LX", or replacing one occurrence of "RX" with "XR". Given the starting string start and the ending string end, return True if and only if there exists a sequence of moves to transform one string to the other.

Example 1:

Input: start = "RXXLRXRXL", end = "XRLXXRRLX"
Output: true
Explanation: We can transform start to end following these steps: RXXLRXRXL -> XRXLRXRXL -> XRLXRXRXL -> XRLXXRRXL -> XRLXXRRLX

Example 2:

Input: start = "X", end = "L"
Output: false


Solution

Solution

The first observation we can make is that the two moves can be described as the following: shift L to the left and shift R to the right. Since L and R cannot be swapped with each other, the relative order of L and R letters will never change.

Let's label L and R as valid letters.

Our first condition for a transformation from start to end is that both start and end must have the same number of valid letters. In addition, the first valid letter in start must match the first valid letter in end, the second valid letter in start must match the second valid letter in end, and so on until the last.

We can also observe that for a transformation to exist, the ithi^{th} valid letter in start must be able to move to the position of the ithi^{th} valid letter in end. We'll denote startIndex as the index of the ithi^{th} valid letter in start and endIndex as the index of the ithi^{th} valid letter in end. There are two cases to consider:

  • The valid letter is L. Since L can only move left, a transformation exists when startIndex >= endIndex.
  • The valid letter is R. Since R can only move right, a transformation exists when startIndex <= endIndex.

We can implement this using the idea of Two Pointers to keep track of startIndex and endIndex for every valid letter.

GIF

Time Complexity

Let's denote NN as the length of both strings start and end.

Since we use Two Pointers to iterate through both strings once, our time complexity is O(N)O(N).

Time Complexity: O(N)O(N)

Space Complexity

Space Complexity: O(1)O(1)

C++ Solution

1class Solution {
2   public:
3    bool canTransform(string start, string end) {
4        int n = start.size();
5        int startIndex = 0;
6        int endIndex = 0;
7        while (startIndex < n || endIndex < n) {
8            while (startIndex < n &&
9                   start[startIndex] == 'X') {  // find next valid letter in start
10                startIndex++;
11            }
12            while (endIndex < n &&
13                   end[endIndex] == 'X') {  // find next valid letter in end
14                endIndex++;
15            }
16            if (startIndex == n && endIndex == n) {  // both reached the end
17                return true;
18            }
19            if (startIndex == n || endIndex == n) {  // different number of valid letters
20                return false;
21            }
22            if (start[startIndex] != end[endIndex]) {  // different valid letter
23                return false;
24            }
25            if (start[startIndex] == 'R' && startIndex > endIndex) {  // wrong direction
26                return false;
27            }
28            if (start[startIndex] == 'L' && startIndex < endIndex) {  // wrong direction
29                return false;
30            }
31            startIndex++;
32            endIndex++;
33        }
34        return true;
35    }
36};

Java Solution

1class Solution {
2    public boolean canTransform(String start, String end) {
3        int n = start.length();
4        int startIndex = 0;
5        int endIndex = 0;
6        while (startIndex < n || endIndex < n) {
7            while (startIndex < n
8                && start.charAt(startIndex) == 'X') { // find next valid letter in start
9                startIndex++;
10            }
11            while (endIndex < n
12                && end.charAt(endIndex) == 'X') { // find next valid letter in end
13                endIndex++;
14            }
15            if (startIndex == n && endIndex == n) { // both reached the end
16                return true;
17            }
18            if (startIndex == n || endIndex == n) { // different number of valid letters
19                return false;
20            }
21            if (start.charAt(startIndex)
22                != end.charAt(endIndex)) { // different valid letter
23                return false;
24            }
25            if (start.charAt(startIndex) == 'R'
26                && startIndex > endIndex) { // wrong direction
27                return false;
28            }
29            if (start.charAt(startIndex) == 'L'
30                && startIndex < endIndex) { // wrong direction
31                return false;
32            }
33            startIndex++;
34            endIndex++;
35        }
36        return true;
37    }
38}

Python Solution

1class Solution:
2    def canTransform(self, start: str, end: str) -> bool:
3        n = len(start)
4        startIndex = 0
5        endIndex = 0
6        while startIndex < n or endIndex < n:
7            while (
8                startIndex < n and start[startIndex] == "X"
9            ):  # find next valid letter in start
10                startIndex += 1
11            while (
12                endIndex < n and end[endIndex] == "X"
13            ):  # find next valid letter in end
14                endIndex += 1
15            if startIndex == n and endIndex == n:  # both reached the end
16                return True
17            if startIndex == n or endIndex == n:  # different number of valid letters
18                return False
19            if start[startIndex] != end[endIndex]:  # different valid letter
20                return False
21            if start[startIndex] == "R" and startIndex > endIndex:  # wrong direction
22                return False
23            if start[startIndex] == "L" and startIndex < endIndex:  # wrong direction
24                return False
25            startIndex += 1
26            endIndex += 1
27        return True
28
Not Sure What to Study? Take the 2-min Quiz to Find Your Missing Piece:

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

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

Is the following code DFS or BFS?

1void search(Node root) {
2  if (!root) return;
3  visit(root);
4  root.visited = true;
5  for (Node node in root.adjacent) {
6    if (!node.visited) {
7      search(node);
8    }
9  }
10}
Not Sure What to Study? Take the 2-min Quiz:

What's the output of running the following function using input [30, 20, 10, 100, 33, 12]?

1def fun(arr: List[int]) -> List[int]:
2    import heapq
3    heapq.heapify(arr)
4    res = []
5    for i in range(3):
6        res.append(heapq.heappop(arr))
7    return res
8
1public static int[] fun(int[] arr) {
2    int[] res = new int[3];
3    PriorityQueue<Integer> heap = new PriorityQueue<>();
4    for (int i = 0; i < arr.length; i++) {
5        heap.add(arr[i]);
6    }
7    for (int i = 0; i < 3; i++) {
8        res[i] = heap.poll();
9    }
10    return res;
11}
12
1class HeapItem {
2    constructor(item, priority = item) {
3        this.item = item;
4        this.priority = priority;
5    }
6}
7
8class MinHeap {
9    constructor() {
10        this.heap = [];
11    }
12
13    push(node) {
14        // insert the new node at the end of the heap array
15        this.heap.push(node);
16        // find the correct position for the new node
17        this.bubble_up();
18    }
19
20    bubble_up() {
21        let index = this.heap.length - 1;
22
23        while (index > 0) {
24            const element = this.heap[index];
25            const parentIndex = Math.floor((index - 1) / 2);
26            const parent = this.heap[parentIndex];
27
28            if (parent.priority <= element.priority) break;
29            // if the parent is bigger than the child then swap the parent and child
30            this.heap[index] = parent;
31            this.heap[parentIndex] = element;
32            index = parentIndex;
33        }
34    }
35
36    pop() {
37        const min = this.heap[0];
38        this.heap[0] = this.heap[this.size() - 1];
39        this.heap.pop();
40        this.bubble_down();
41        return min;
42    }
43
44    bubble_down() {
45        let index = 0;
46        let min = index;
47        const n = this.heap.length;
48
49        while (index < n) {
50            const left = 2 * index + 1;
51            const right = left + 1;
52
53            if (left < n && this.heap[left].priority < this.heap[min].priority) {
54                min = left;
55            }
56            if (right < n && this.heap[right].priority < this.heap[min].priority) {
57                min = right;
58            }
59            if (min === index) break;
60            [this.heap[min], this.heap[index]] = [this.heap[index], this.heap[min]];
61            index = min;
62        }
63    }
64
65    peek() {
66        return this.heap[0];
67    }
68
69    size() {
70        return this.heap.length;
71    }
72}
73
74function fun(arr) {
75    const heap = new MinHeap();
76    for (const x of arr) {
77        heap.push(new HeapItem(x));
78    }
79    const res = [];
80    for (let i = 0; i < 3; i++) {
81        res.push(heap.pop().item);
82    }
83    return res;
84}
85
Fast Track Your Learning with Our Quick Skills Quiz:

What's the relationship between a tree and a graph?


Recommended Readings


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫