Facebook Pixel

657. Robot Return to Origin

EasyStringSimulation
Leetcode Link

Problem Description

You have a robot that starts at position (0, 0) on a 2D plane. The robot can move in four directions based on commands given as a string:

  • 'U' moves the robot up (increases y-coordinate by 1)
  • 'D' moves the robot down (decreases y-coordinate by 1)
  • 'L' moves the robot left (decreases x-coordinate by 1)
  • 'R' moves the robot right (increases x-coordinate by 1)

Given a string moves containing a sequence of these movement commands, determine if the robot returns to its starting position (0, 0) after executing all the moves.

For example:

  • If moves = "UD", the robot moves up then down, returning to (0, 0), so return true
  • If moves = "LL", the robot moves left twice, ending at (-2, 0), so return false

The solution tracks the robot's position using coordinates (x, y). For each move command in the string, it updates the coordinates accordingly. After processing all moves, if both x and y equal 0, the robot has returned to the origin.

The key insight is that for the robot to return to the origin, the total displacement in both the horizontal and vertical directions must be zero. This means the number of 'L' moves must equal the number of 'R' moves, and the number of 'U' moves must equal the number of 'D' moves.

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

Intuition

Think about what it means for the robot to return to its starting position. If the robot ends up where it started, then its total displacement must be zero in both dimensions.

Consider the robot's movement on a coordinate plane. Each move changes the robot's position:

  • Moving right increases x-coordinate, moving left decreases it
  • Moving up increases y-coordinate, moving down decreases it

For the robot to return to (0, 0), whatever distance it traveled in one direction must be canceled out by traveling the same distance in the opposite direction. This means:

  • Every 'R' move (x + 1) needs to be balanced by an 'L' move (x - 1)
  • Every 'U' move (y + 1) needs to be balanced by a 'D' move (y - 1)

Instead of counting occurrences of each character, we can simulate the actual movement. Start at (0, 0) and track the position as we process each move. This is more intuitive because we're directly modeling what the robot does - we move step by step and see where we end up.

The beauty of this approach is its simplicity: we don't need to think about complex patterns or optimize anything. We just follow the robot's path and check if it comes back home. If after all moves x = 0 and y = 0, then the robot made a complete circle back to the origin.

Solution Approach

We maintain a coordinate (x, y) to represent the robot's position on the 2D plane, starting at (0, 0).

The implementation follows these steps:

  1. Initialize coordinates: Set x = 0 and y = 0 to represent the starting position at the origin.

  2. Process each move: Traverse through each character c in the moves string and update the coordinates based on the movement direction:

    • If c is 'U': increment y by 1 (move up)
    • If c is 'D': decrement y by 1 (move down)
    • If c is 'L': decrement x by 1 (move left)
    • If c is 'R': increment x by 1 (move right)
  3. Check final position: After processing all moves, check if both x == 0 and y == 0. If true, the robot has returned to the origin.

The solution uses a match statement (Python 3.10+) for clean branching based on the move character. This could alternatively be implemented using if-elif statements or a dictionary mapping.

The time complexity is O(n) where n is the length of the moves string, as we traverse it once. The space complexity is O(1) since we only use two variables to track the position regardless of input size.

The algorithm essentially simulates the robot's movement in real-time, tracking its displacement from the origin. If the final displacement is (0, 0), the robot has made a complete circle back to where it started.

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

Let's walk through the solution with moves = "UDLR":

Step 1: Initialize position

  • Start at origin: x = 0, y = 0
  • Current position: (0, 0)

Step 2: Process each move

Move 1: 'U' (Up)

  • Update: y = y + 1 = 0 + 1 = 1
  • Current position: (0, 1)

Move 2: 'D' (Down)

  • Update: y = y - 1 = 1 - 1 = 0
  • Current position: (0, 0)

Move 3: 'L' (Left)

  • Update: x = x - 1 = 0 - 1 = -1
  • Current position: (-1, 0)

Move 4: 'R' (Right)

  • Update: x = x + 1 = -1 + 1 = 0
  • Current position: (0, 0)

Step 3: Check final position

  • Final coordinates: x = 0, y = 0
  • Since both x and y equal 0, the robot returned to origin
  • Return: true

Let's trace another example where the robot doesn't return: moves = "LLU":

Step 1: Initialize

  • Start: x = 0, y = 0

Step 2: Process moves

  • Move 1 'L': x = -1, position: (-1, 0)
  • Move 2 'L': x = -2, position: (-2, 0)
  • Move 3 'U': y = 1, position: (-2, 1)

Step 3: Check final position

  • Final: x = -2, y = 1
  • Not at origin (0, 0)
  • Return: false

The key insight: each move affects exactly one coordinate by exactly 1 unit. For the robot to return home, all movements must cancel out perfectly.

Solution Implementation

1class Solution:
2    def judgeCircle(self, moves: str) -> bool:
3        """
4        Determines if a sequence of moves returns to the origin (0, 0).
5      
6        Args:
7            moves: A string containing move commands ('U', 'D', 'L', 'R')
8      
9        Returns:
10            True if the final position is (0, 0), False otherwise
11        """
12        # Initialize coordinates at origin
13        x_position = 0
14        y_position = 0
15      
16        # Process each move command
17        for move in moves:
18            if move == "U":  # Move up
19                y_position += 1
20            elif move == "D":  # Move down
21                y_position -= 1
22            elif move == "L":  # Move left
23                x_position -= 1
24            elif move == "R":  # Move right
25                x_position += 1
26      
27        # Check if we returned to origin
28        return x_position == 0 and y_position == 0
29
1class Solution {
2    /**
3     * Determines if a sequence of moves returns to the origin position.
4     * Starting from origin (0, 0), each move changes the position:
5     * 'U' (Up): moves up by increasing y-coordinate
6     * 'D' (Down): moves down by decreasing y-coordinate
7     * 'L' (Left): moves left by decreasing x-coordinate
8     * 'R' (Right): moves right by increasing x-coordinate
9     * 
10     * @param moves String containing sequence of moves (U, D, L, R)
11     * @return true if the final position is origin (0, 0), false otherwise
12     */
13    public boolean judgeCircle(String moves) {
14        // Initialize coordinates at origin
15        int xCoordinate = 0;
16        int yCoordinate = 0;
17      
18        // Process each move character
19        for (char move : moves.toCharArray()) {
20            switch (move) {
21                case 'U':  // Move up
22                    yCoordinate++;
23                    break;
24                case 'D':  // Move down
25                    yCoordinate--;
26                    break;
27                case 'L':  // Move left
28                    xCoordinate--;
29                    break;
30                case 'R':  // Move right
31                    xCoordinate++;
32                    break;
33                default:
34                    // Invalid move character, ignore
35                    break;
36            }
37        }
38      
39        // Check if returned to origin
40        return xCoordinate == 0 && yCoordinate == 0;
41    }
42}
43
1class Solution {
2public:
3    /**
4     * Determines if a robot returns to the origin after executing all moves.
5     * The robot starts at position (0, 0) on a 2D plane.
6     * 
7     * @param moves A string containing move commands:
8     *              'U' - move up (increase y-coordinate)
9     *              'D' - move down (decrease y-coordinate)
10     *              'L' - move left (decrease x-coordinate)
11     *              'R' - move right (increase x-coordinate)
12     * @return true if the robot returns to origin (0, 0), false otherwise
13     */
14    bool judgeCircle(string moves) {
15        // Initialize coordinates to track robot's position
16        int xCoordinate = 0;
17        int yCoordinate = 0;
18      
19        // Process each move command
20        for (char move : moves) {
21            switch (move) {
22                case 'U':  // Move up
23                    yCoordinate++;
24                    break;
25                case 'D':  // Move down
26                    yCoordinate--;
27                    break;
28                case 'L':  // Move left
29                    xCoordinate--;
30                    break;
31                case 'R':  // Move right
32                    xCoordinate++;
33                    break;
34            }
35        }
36      
37        // Check if robot returned to origin
38        return (xCoordinate == 0 && yCoordinate == 0);
39    }
40};
41
1/**
2 * Determines if a sequence of moves returns to the origin point (0, 0)
3 * @param moves - String containing move commands: 'U' (up), 'D' (down), 'L' (left), 'R' (right)
4 * @returns true if the moves return to origin, false otherwise
5 */
6function judgeCircle(moves: string): boolean {
7    // Initialize coordinates at origin
8    let xCoordinate: number = 0;
9    let yCoordinate: number = 0;
10  
11    // Process each move command
12    for (const move of moves) {
13        switch (move) {
14            case 'U':
15                // Move up: increase y-coordinate
16                yCoordinate++;
17                break;
18            case 'D':
19                // Move down: decrease y-coordinate
20                yCoordinate--;
21                break;
22            case 'L':
23                // Move left: decrease x-coordinate
24                xCoordinate--;
25                break;
26            case 'R':
27                // Move right: increase x-coordinate
28                xCoordinate++;
29                break;
30        }
31    }
32  
33    // Check if we returned to the origin
34    return xCoordinate === 0 && yCoordinate === 0;
35}
36

Time and Space Complexity

Time Complexity: O(n), where n is the length of the string moves. The algorithm iterates through each character in the input string exactly once using a single for loop. Each operation inside the loop (the match statement and the increment/decrement operations) takes constant time O(1). Therefore, the total time complexity is O(n) * O(1) = O(n).

Space Complexity: O(1). The algorithm uses only two integer variables x and y to track the position, regardless of the input size. The space usage remains constant and does not grow with the length of the input string. The loop variable c also uses constant space. Since no additional data structures are created that scale with the input, the space complexity is O(1).

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

Common Pitfalls

1. Inefficient String Operations in Other Approaches

A common pitfall is attempting to optimize by counting characters using multiple passes through the string or using string methods like count():

# Inefficient approach - multiple passes
def judgeCircle(moves: str) -> bool:
    return moves.count('U') == moves.count('D') and \
           moves.count('L') == moves.count('R')

While this works correctly and is more concise, it makes 4 passes through the string (one for each count() call), resulting in O(4n) operations compared to the single-pass O(n) simulation approach.

2. Using Complex Data Structures Unnecessarily

Some might overcomplicate by using dictionaries or collections.Counter:

# Overly complex for this problem
from collections import Counter
def judgeCircle(moves: str) -> bool:
    counts = Counter(moves)
    return counts['U'] == counts['D'] and counts['L'] == counts['R']

This adds unnecessary overhead for a simple problem where two integer variables suffice.

3. Incorrect Coordinate System Understanding

A subtle pitfall is mixing up which direction corresponds to positive/negative changes:

# Wrong direction mapping
for move in moves:
    if move == "U":
        y_position -= 1  # Wrong! Should be += 1
    elif move == "D":
        y_position += 1  # Wrong! Should be -= 1

4. Not Handling Edge Cases

Forgetting to consider edge cases like:

  • Empty string (should return True as robot hasn't moved)
  • Single character (will always return False unless empty)

Solution to avoid these pitfalls:

  • Stick with the simple simulation approach for clarity and single-pass efficiency
  • Use consistent coordinate conventions (up = +y, right = +x)
  • Consider using a direction dictionary for cleaner code if preferred:
def judgeCircle(moves: str) -> bool:
    x, y = 0, 0
    directions = {'U': (0, 1), 'D': (0, -1), 'L': (-1, 0), 'R': (1, 0)}
  
    for move in moves:
        if move in directions:
            dx, dy = directions[move]
            x += dx
            y += dy
  
    return x == 0 and y == 0
Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

Which of the following uses divide and conquer strategy?


Recommended Readings

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

Load More