Facebook Pixel

1041. Robot Bounded In Circle

MediumMathStringSimulation
Leetcode Link

Problem Description

You have a robot on an infinite 2D plane that starts at position (0, 0) facing north. The coordinate system is defined as:

  • North: positive y-axis direction
  • South: negative y-axis direction
  • East: positive x-axis direction
  • West: negative x-axis direction

The robot can execute three types of instructions:

  • "G": Move forward 1 unit in the current direction
  • "L": Turn 90 degrees left (counter-clockwise)
  • "R": Turn 90 degrees right (clockwise)

Given a string instructions containing these commands, the robot will execute the entire instruction sequence and then repeat it infinitely.

Your task is to determine if the robot will stay within a bounded circle on the plane. Return true if the robot's path is bounded (never leaves some circle), and false otherwise.

For example:

  • If instructions = "GGLLGG", the robot moves forward 2 units, turns left twice (facing south), then moves forward 2 units. After one cycle, it returns to (0, 0) facing south. Since it returns to origin, it will stay bounded.
  • If instructions = "GG", the robot keeps moving north indefinitely, so it's not bounded.

The key insight is that after executing the instruction sequence once, if either:

  1. The robot returns to the origin (0, 0), OR
  2. The robot faces a different direction than north

Then the robot will stay within a bounded circle. This is because changing direction means the robot will eventually trace a closed path after multiple cycles (at most 4 cycles for a full rotation back to north).

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

Intuition

The key insight comes from observing what happens when we repeat the instruction sequence multiple times. Let's think about what determines whether the robot stays bounded:

After executing the instructions once, the robot ends up at some position (x, y) facing some direction. Now we need to understand when repeating this pattern keeps the robot bounded.

Case 1: Robot returns to origin after one cycle If after one execution the robot is back at (0, 0), then no matter which direction it faces, repeating the instructions will just keep bringing it back to origin. The robot traces a closed loop and stays bounded.

Case 2: Robot doesn't return to origin This is more interesting. The robot is at position (x, y) after one cycle. What happens next depends on which direction it's facing:

  • Still facing North (same as start): The second execution will move the robot by another (x, y), third by another (x, y), and so on. The position after n cycles becomes (n*x, n*y). Since x or y is non-zero (not at origin), the robot keeps moving away infinitely - unbounded.

  • Facing West (turned left once): After the second cycle, the displacement is (-y, x) relative to first cycle's end. After the third cycle, it's (-x, -y). After the fourth, it's (y, -x). Adding these up: (x, y) + (-y, x) + (-x, -y) + (y, -x) = (0, 0). The robot returns to origin after 4 cycles - bounded.

  • Facing South (turned 180°): The second cycle moves the robot by (-x, -y). Combined with the first cycle's (x, y), we get back to (0, 0) after just 2 cycles - bounded.

  • Facing East (turned right once): Similar to facing west, after 4 cycles the displacements cancel out and return to origin - bounded.

The pattern emerges: if the robot changes direction after one cycle, the different orientations cause the displacement vectors to eventually cancel out (after at most 4 cycles). But if it maintains the original direction, it keeps accumulating displacement in the same direction forever.

Therefore, we can determine boundedness by checking after one execution:

  1. Did we return to origin? (distances cancel: dist[North] == dist[South] and dist[East] == dist[West])
  2. OR did we change direction? (k != 0 where k represents the final direction)

If either condition is true, the robot stays bounded.

Learn more about Math patterns.

Solution Approach

The implementation uses a simulation approach to track the robot's movement through one complete execution of the instruction sequence.

Data Structures:

  • k: An integer representing the robot's current direction (0=North, 1=West, 2=South, 3=East)
  • dist: An array of size 4 to track the distance traveled in each direction

Algorithm Steps:

  1. Initialize the tracking variables:

    • Set k = 0 (robot starts facing North)
    • Create dist = [0, 0, 0, 0] to track distances in each direction
  2. Process each instruction: For each character c in the instruction string:

    • Left turn ('L'): Update direction by k = (k + 1) % 4
      • This rotates counter-clockwise through: North→West→South→East→North
    • Right turn ('R'): Update direction by k = (k + 3) % 4
      • Adding 3 is equivalent to subtracting 1 in modulo 4, rotating clockwise
      • This rotates through: North→East→South→West→North
    • Go forward ('G'): Increment the distance in current direction by dist[k] += 1
  3. Check boundedness conditions: After processing all instructions, return true if either:

    • Robot returns to origin: dist[0] == dist[2] and dist[1] == dist[3]
      • dist[0] (North) equals dist[2] (South): net vertical displacement is 0
      • dist[1] (West) equals dist[3] (East): net horizontal displacement is 0
    • OR robot changed direction: k != 0
      • The robot is no longer facing North after one cycle

Why this works:

  • If the robot moves North n times and South n times, the vertical movements cancel out
  • If the robot moves East m times and West m times, the horizontal movements cancel out
  • Together, these conditions check if the robot returns to (0, 0)
  • If the robot doesn't return to origin but changed direction (k != 0), we know from our analysis that after at most 4 cycles, the displacement vectors will cancel out due to rotation

The elegance of this solution is that we don't need to simulate multiple cycles - just one execution tells us everything we need to determine boundedness.

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 trace through the example instructions = "GGLLGG" to see how our solution determines boundedness.

Initial State:

  • Direction k = 0 (facing North)
  • Distance array dist = [0, 0, 0, 0] representing [North, West, South, East]

Processing each instruction:

  1. 'G' (1st): Move forward while facing North

    • dist[0] += 1dist = [1, 0, 0, 0]
    • Robot position: (0, 1)
  2. 'G' (2nd): Move forward while still facing North

    • dist[0] += 1dist = [2, 0, 0, 0]
    • Robot position: (0, 2)
  3. 'L' (3rd): Turn left

    • k = (0 + 1) % 4 = 1 (now facing West)
    • No position change
  4. 'L' (4th): Turn left again

    • k = (1 + 1) % 4 = 2 (now facing South)
    • No position change
  5. 'G' (5th): Move forward while facing South

    • dist[2] += 1dist = [2, 0, 1, 0]
    • Robot position: (0, 1)
  6. 'G' (6th): Move forward while still facing South

    • dist[2] += 1dist = [2, 0, 2, 0]
    • Robot position: (0, 0)

Final Check:

  • Final direction: k = 2 (facing South, not North)
  • Final distances: dist = [2, 0, 2, 0]

Boundedness conditions:

  1. Check if at origin:

    • dist[0] == dist[2]? → 2 == 2 ✓ (North and South distances equal)
    • dist[1] == dist[3]? → 0 == 0 ✓ (West and East distances equal)
    • Robot returned to origin!
  2. Check if direction changed:

    • k != 0? → 2 != 0 ✓ (Also true - facing South, not North)

Since the robot returns to origin (condition 1 is satisfied), the function returns true. The robot will stay bounded, repeatedly tracing the same rectangular path: up 2, turn around, down 2, and back to start.

Solution Implementation

1class Solution:
2    def isRobotBounded(self, instructions: str) -> bool:
3        # Direction index: 0=North, 1=West, 2=South, 3=East
4        direction = 0
5      
6        # Track distance moved in each direction
7        distance_per_direction = [0] * 4
8      
9        # Process each instruction
10        for instruction in instructions:
11            if instruction == 'L':
12                # Turn left: rotate 90 degrees counter-clockwise
13                direction = (direction + 1) % 4
14            elif instruction == 'R':
15                # Turn right: rotate 90 degrees clockwise
16                # Adding 3 is equivalent to subtracting 1 with modulo 4
17                direction = (direction + 3) % 4
18            else:  # instruction == 'G'
19                # Move forward in current direction
20                distance_per_direction[direction] += 1
21      
22        # Robot forms a circle if:
23        # 1. It returns to origin (North-South distances equal AND East-West distances equal)
24        # 2. OR it's not facing North after one cycle (will eventually circle back)
25        is_at_origin = (distance_per_direction[0] == distance_per_direction[2] and 
26                        distance_per_direction[1] == distance_per_direction[3])
27        is_not_facing_north = direction != 0
28      
29        return is_at_origin or is_not_facing_north
30
1class Solution {
2    public boolean isRobotBounded(String instructions) {
3        // Direction index: 0=North, 1=West, 2=South, 3=East
4        int direction = 0;
5      
6        // Array to track distance moved in each direction
7        // distanceInDirection[0] = North, distanceInDirection[1] = West
8        // distanceInDirection[2] = South, distanceInDirection[3] = East
9        int[] distanceInDirection = new int[4];
10      
11        // Process each instruction in the sequence
12        for (int i = 0; i < instructions.length(); i++) {
13            char instruction = instructions.charAt(i);
14          
15            if (instruction == 'L') {
16                // Turn left: rotate 90 degrees counter-clockwise
17                // North(0) -> West(1) -> South(2) -> East(3) -> North(0)
18                direction = (direction + 1) % 4;
19            } else if (instruction == 'R') {
20                // Turn right: rotate 90 degrees clockwise
21                // North(0) -> East(3) -> South(2) -> West(1) -> North(0)
22                // Adding 3 is equivalent to subtracting 1 in modulo 4
23                direction = (direction + 3) % 4;
24            } else {
25                // instruction == 'G': move forward one unit in current direction
26                distanceInDirection[direction]++;
27            }
28        }
29      
30        // Robot forms a circle if:
31        // 1. Net displacement is zero (North steps = South steps AND West steps = East steps)
32        // 2. Robot is not facing North after instructions (will eventually return to origin)
33        return (distanceInDirection[0] == distanceInDirection[2] && 
34                distanceInDirection[1] == distanceInDirection[3]) || 
35               (direction != 0);
36    }
37}
38
1class Solution {
2public:
3    bool isRobotBounded(string instructions) {
4        // Track distance moved in each of the 4 directions (North, West, South, East)
5        int distance[4] = {0, 0, 0, 0};
6      
7        // Current direction: 0=North, 1=West, 2=South, 3=East
8        int currentDirection = 0;
9      
10        // Process each instruction
11        for (char& instruction : instructions) {
12            if (instruction == 'L') {
13                // Turn left: rotate 90 degrees counterclockwise
14                currentDirection = (currentDirection + 1) % 4;
15            } else if (instruction == 'R') {
16                // Turn right: rotate 90 degrees clockwise
17                // Adding 3 is equivalent to subtracting 1 in modulo 4
18                currentDirection = (currentDirection + 3) % 4;
19            } else {
20                // Move forward ('G'): increment distance in current direction
21                ++distance[currentDirection];
22            }
23        }
24      
25        // Robot forms a circle if:
26        // 1. It returns to origin (North-South distances cancel, East-West distances cancel), OR
27        // 2. It's not facing North after one cycle (will eventually return after 2 or 4 cycles)
28        return (distance[0] == distance[2] && distance[1] == distance[3]) || currentDirection != 0;
29    }
30};
31
1/**
2 * Determines if a robot returns to origin or stays in a bounded circle
3 * after executing the given instructions infinitely.
4 * 
5 * @param instructions - String containing 'G' (go straight), 'L' (turn left), 'R' (turn right)
6 * @returns true if robot is bounded in a circle, false otherwise
7 */
8function isRobotBounded(instructions: string): boolean {
9    // Track distance moved in each direction: North, East, South, West
10    // Index 0: North, 1: East, 2: South, 3: West
11    const distanceInEachDirection: number[] = new Array(4).fill(0);
12  
13    // Current direction the robot is facing (0: North, 1: East, 2: South, 3: West)
14    let currentDirection: number = 0;
15  
16    // Process each instruction
17    for (const instruction of instructions) {
18        if (instruction === 'L') {
19            // Turn left: rotate 90 degrees counter-clockwise
20            currentDirection = (currentDirection + 1) % 4;
21        } else if (instruction === 'R') {
22            // Turn right: rotate 90 degrees clockwise
23            // Adding 3 is equivalent to subtracting 1 in modulo 4
24            currentDirection = (currentDirection + 3) % 4;
25        } else {
26            // Move forward ('G'): increment distance in current direction
27            distanceInEachDirection[currentDirection]++;
28        }
29    }
30  
31    // Robot is bounded if:
32    // 1. It returns to origin (net displacement is zero in both axes)
33    //    North-South displacement: distanceInEachDirection[0] - distanceInEachDirection[2] = 0
34    //    East-West displacement: distanceInEachDirection[1] - distanceInEachDirection[3] = 0
35    // 2. OR it doesn't face north after one cycle (will eventually return after 2 or 4 cycles)
36    return (distanceInEachDirection[0] === distanceInEachDirection[2] && 
37            distanceInEachDirection[1] === distanceInEachDirection[3]) || 
38            currentDirection !== 0;
39}
40

Time and Space Complexity

Time Complexity: O(n)

The algorithm iterates through the instructions string exactly once. For each character in the string, it performs constant-time operations:

  • Checking the character type ('L', 'R', or 'G')
  • Updating the direction k with modulo operations
  • Incrementing a distance counter

Since we process each of the n characters exactly once with O(1) operations per character, the overall time complexity is O(n), where n is the length of the instruction string.

Space Complexity: O(1)

The algorithm uses a fixed amount of extra space:

  • Variable k to track the current direction (single integer)
  • Array dist of fixed size 4 to track distances in each direction

The space usage does not depend on the input size n. The dist array always has exactly 4 elements regardless of how long the instructions string is. Therefore, the space complexity is O(1) (constant space).

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

Common Pitfalls

1. Incorrect Direction Mapping Leading to Wrong Distance Tracking

One of the most common mistakes is using an inconsistent or incorrect mapping between direction indices and actual compass directions. This leads to recording movements in the wrong direction slots.

Pitfall Example:

# Incorrect mapping where developers might use:
# 0=North, 1=East, 2=South, 3=West (clockwise order)
# But then use the wrong rotation logic

direction = 0  # North
distance = [0] * 4

for instruction in instructions:
    if instruction == 'L':
        direction = (direction + 1) % 4  # This would go North->East (wrong!)
    elif instruction == 'R':
        direction = (direction - 1) % 4  # This would go North->West (wrong!)
    else:
        distance[direction] += 1

Why it's wrong: The rotation logic doesn't match the direction mapping. If you use clockwise ordering (N→E→S→W), then turning left should decrease the index, not increase it.

Solution: Stick to one consistent mapping and ensure your rotation logic matches:

Option 1 - Counter-clockwise mapping (as in the original solution):

# 0=North, 1=West, 2=South, 3=East
# Left turn: (direction + 1) % 4
# Right turn: (direction + 3) % 4

Option 2 - Clockwise mapping:

# 0=North, 1=East, 2=South, 3=West
# Left turn: (direction + 3) % 4  # or (direction - 1 + 4) % 4
# Right turn: (direction + 1) % 4

2. Using Coordinate Tracking Instead of Direction-Based Distance

Another pitfall is trying to track the exact (x, y) position and getting confused with direction changes.

Pitfall Example:

def isRobotBounded(self, instructions: str) -> bool:
    x, y = 0, 0
    dx, dy = 0, 1  # Initially facing north
  
    for instruction in instructions:
        if instruction == 'L':
            # Incorrect rotation transformation
            dx, dy = dy, dx  # Wrong! This doesn't rotate 90 degrees
        elif instruction == 'R':
            dx, dy = -dy, dx  # May have signs wrong
        else:
            x += dx
            y += dy
  
    return (x == 0 and y == 0) or (dx != 0 or dy != 1)

Why it's wrong: The rotation transformations for (dx, dy) are easy to get wrong. The correct 90-degree rotations are:

  • Left turn: (dx, dy) -> (-dy, dx)
  • Right turn: (dx, dy) -> (dy, -dx)

Solution: If using coordinate approach, ensure correct rotation matrices:

def isRobotBounded(self, instructions: str) -> bool:
    x, y = 0, 0
    dx, dy = 0, 1  # Initially facing north
  
    for instruction in instructions:
        if instruction == 'L':
            dx, dy = -dy, dx  # Correct 90° counter-clockwise
        elif instruction == 'R':
            dx, dy = dy, -dx  # Correct 90° clockwise
        else:
            x += dx
            y += dy
  
    return (x == 0 and y == 0) or (dx != 0 or dy != 1)

3. Forgetting to Check Both Conditions for Boundedness

Pitfall Example:

def isRobotBounded(self, instructions: str) -> bool:
    # ... simulation code ...
  
    # Only checking if robot returns to origin
    return distance[0] == distance[2] and distance[1] == distance[3]
    # Forgot to check if direction changed!

Why it's wrong: The robot can be bounded even if it doesn't return to origin after one cycle, as long as it's facing a different direction. After at most 4 cycles, it will form a closed loop.

Solution: Always check both conditions:

return (distance[0] == distance[2] and distance[1] == distance[3]) or direction != 0
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of these pictures shows the visit order of a depth-first search?


Recommended Readings

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

Load More