Facebook Pixel

2731. Movement of Robots

Problem Description

You have robots standing on an infinite number line at initial positions given by the array nums. Each robot will move when given a command based on the direction string s:

  • 'L' means the robot moves left (negative direction) at 1 unit per second
  • 'R' means the robot moves right (positive direction) at 1 unit per second

When two robots collide (occupy the same position), they instantly reverse their directions and continue moving. The collision happens instantaneously without any time delay.

Given:

  • nums: initial positions of robots (0-indexed array)
  • s: string of directions where s[i] is the direction for robot i
  • d: number of seconds to simulate

You need to find the sum of distances between all pairs of robots after d seconds. Return the result modulo 10^9 + 7.

Key insights about collisions:

  • When robots collide, they swap directions immediately
  • If robot at position 0 moving right meets robot at position 2 moving left:
    • At t=1: both are at position 1 and collide
    • After collision: first robot moves left, second robot moves right
    • At t=2: first robot is at position 0, second robot is at position 2

Important notes:

  • Each pair of robots is counted only once (pair (i,j) is the same as (j,i))
  • The sum can be very large, so return it modulo 10^9 + 7

The clever observation in the solution is that when two robots collide and swap directions, it's mathematically equivalent to them passing through each other without collision. This means we can simply calculate each robot's final position after d seconds without worrying about collisions, then compute the sum of pairwise distances.

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

Intuition

The key insight is recognizing that when two robots collide and swap directions, the overall system behaves as if they simply passed through each other.

Consider two robots: Robot A at position 0 moving right, and Robot B at position 2 moving left. After 1 second, they meet at position 1 and collide. After the collision, Robot A moves left and Robot B moves right. After 2 seconds total, Robot A is back at position 0 and Robot B is at position 2.

Now imagine if they just passed through each other without colliding: Robot A would be at position 2 and Robot B would be at position 0 after 2 seconds. Notice that the set of final positions is the same {0, 2} in both cases - only the robot identities have swapped!

This observation is crucial: for calculating the sum of pairwise distances, we don't care which specific robot is at which position. We only care about the set of final positions. The sum of distances between all pairs depends only on the positions themselves, not on which robot occupies which position.

Therefore, we can ignore collisions entirely and simply:

  1. Calculate where each robot would be after d seconds if there were no collisions
  2. Sort these final positions
  3. Calculate the sum of pairwise distances

For the distance calculation, after sorting the positions, we can efficiently compute the sum by observing that for position nums[i], it contributes to the total sum by:

  • Being subtracted i times (for all robots to its left)
  • Being added (n-i-1) times (for all robots to its right)

This can be simplified to: for each position x at index i, its contribution is i * x - sum_of_positions_before_i.

Learn more about Prefix Sum and Sorting patterns.

Solution Approach

Based on our intuition that collisions can be ignored, the implementation follows these steps:

Step 1: Calculate Final Positions

for i, c in enumerate(s):
    nums[i] += d if c == "R" else -d

For each robot at index i, we update its position based on its direction:

  • If moving right ('R'), add d to its position
  • If moving left ('L'), subtract d from its position

This gives us the final position of each robot after d seconds, ignoring collisions.

Step 2: Sort the Positions

nums.sort()

We sort the final positions to prepare for efficient distance calculation. Sorting allows us to use a mathematical trick to compute the sum of pairwise distances in linear time.

Step 3: Calculate Sum of Pairwise Distances

ans = s = 0
for i, x in enumerate(nums):
    ans += i * x - s
    s += x

This is the clever part. For each position x at index i in the sorted array:

  • The distance from x to all positions before it is: x - nums[0] + x - nums[1] + ... + x - nums[i-1]
  • This simplifies to: i * x - (nums[0] + nums[1] + ... + nums[i-1])
  • We maintain a running sum s of all previous positions to avoid recalculating

The formula i * x - s computes the total distance from position x to all positions that come before it in the sorted array.

Step 4: Return Result Modulo

return ans % mod

Since the sum can be very large, we return the result modulo 10^9 + 7.

Time Complexity: O(n log n) due to sorting, where n is the number of robots. Space Complexity: O(1) if we modify the input array in-place, or O(n) if we need to preserve the original array.

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 a concrete example to illustrate the solution approach.

Given:

  • nums = [1, 0] (initial positions)
  • s = "RL" (Robot 0 moves Right, Robot 1 moves Left)
  • d = 2 (simulate for 2 seconds)

Step 1: Calculate Final Positions (ignoring collisions)

For Robot 0 at position 1 moving Right ('R'):

  • Final position = 1 + 2 = 3

For Robot 1 at position 0 moving Left ('L'):

  • Final position = 0 - 2 = -2

After this step: nums = [3, -2]

Step 2: Sort the Positions

Sort the array: nums = [-2, 3]

Step 3: Calculate Sum of Pairwise Distances

We need to find the distance between all pairs. With sorted positions [-2, 3], there's only one pair:

  • Distance between -2 and 3 = |3 - (-2)| = 5

Using our formula approach:

  • Initialize: ans = 0, s = 0
  • For index 0, position -2:
    • ans += 0 * (-2) - 0 = 0
    • s += -2, so s = -2
  • For index 1, position 3:
    • ans += 1 * 3 - (-2) = 3 + 2 = 5
    • s += 3, so s = 1

Step 4: Return Result

ans = 5, return 5 % (10^9 + 7) = 5

Verification with actual collisions: Let's verify this makes sense by tracking what actually happens with collisions:

  • t=0: Robot 0 at position 1 (moving R), Robot 1 at position 0 (moving L)
  • t=0.5: Both robots meet at position 0.5 and collide
  • After collision: Robot 0 now moves L, Robot 1 now moves R
  • t=2: Robot 0 at position -2, Robot 1 at position 3

The final positions are {-2, 3}, which matches our calculation! The distance between them is indeed 5.

This demonstrates that ignoring collisions and just calculating final positions gives us the correct set of positions for computing distances.

Solution Implementation

1class Solution:
2    def sumDistance(self, nums: List[int], s: str, d: int) -> int:
3        # Define modulo constant for large number handling
4        MOD = 10**9 + 7
5      
6        # Update positions based on direction
7        # 'R' means move right (add d), 'L' means move left (subtract d)
8        for i, direction in enumerate(s):
9            if direction == "R":
10                nums[i] += d
11            else:  # direction == "L"
12                nums[i] -= d
13      
14        # Sort positions to calculate distances efficiently
15        nums.sort()
16      
17        # Initialize variables for calculating sum of distances
18        total_distance = 0  # Final answer
19        prefix_sum = 0      # Running sum of positions
20      
21        # Calculate sum of all pairwise distances
22        # For position at index i, its contribution is:
23        # i * nums[i] - sum of all previous positions
24        for i, position in enumerate(nums):
25            total_distance += i * position - prefix_sum
26            prefix_sum += position
27      
28        # Return result with modulo applied
29        return total_distance % MOD
30
1class Solution {
2    public int sumDistance(int[] nums, String s, int d) {
3        int n = nums.length;
4        long[] finalPositions = new long[n];
5      
6        // Calculate final position for each robot after d units of time
7        // Robots moving left subtract d, robots moving right add d
8        for (int i = 0; i < n; i++) {
9            if (s.charAt(i) == 'L') {
10                finalPositions[i] = (long) nums[i] - d;
11            } else {
12                finalPositions[i] = (long) nums[i] + d;
13            }
14        }
15      
16        // Sort positions to efficiently calculate pairwise distances
17        Arrays.sort(finalPositions);
18      
19        // Calculate sum of all pairwise distances using mathematical optimization
20        // For sorted array, distance contribution of position i is:
21        // i * finalPositions[i] - sum of all positions before i
22        long totalDistance = 0;
23        long prefixSum = 0;
24        final int MOD = 1000000007;
25      
26        for (int i = 0; i < n; i++) {
27            // Current position contributes to distance with all previous positions
28            // Contribution = (number of previous positions) * current - sum of previous
29            totalDistance = (totalDistance + i * finalPositions[i] - prefixSum) % MOD;
30          
31            // Update prefix sum for next iteration
32            prefixSum += finalPositions[i];
33        }
34      
35        return (int) totalDistance;
36    }
37}
38
1class Solution {
2public:
3    int sumDistance(vector<int>& nums, string s, int d) {
4        int n = nums.size();
5      
6        // Calculate final positions after d seconds
7        // Robots moving left subtract d, robots moving right add d
8        vector<long long> finalPositions(n);
9        for (int i = 0; i < n; ++i) {
10            if (s[i] == 'L') {
11                finalPositions[i] = static_cast<long long>(nums[i]) - d;
12            } else {
13                finalPositions[i] = static_cast<long long>(nums[i]) + d;
14            }
15        }
16      
17        // Sort positions to efficiently calculate pairwise distances
18        sort(finalPositions.begin(), finalPositions.end());
19      
20        // Calculate sum of all pairwise distances using prefix sum technique
21        // For position at index i, it contributes to the distance sum as:
22        // (i * finalPositions[i] - prefixSum) where prefixSum is sum of all positions before i
23        long long totalDistance = 0;
24        long long prefixSum = 0;
25        const int MOD = 1000000007;
26      
27        for (int i = 0; i < n; ++i) {
28            // Current position contributes positive distance to all positions before it
29            // and negative distance from all positions after it
30            totalDistance = (totalDistance + i * finalPositions[i] - prefixSum) % MOD;
31            prefixSum += finalPositions[i];
32        }
33      
34        return static_cast<int>(totalDistance);
35    }
36};
37
1/**
2 * Calculates the sum of distances between all pairs of robots after movement
3 * @param nums - Array of initial positions of robots
4 * @param s - String where s[i] is 'L' or 'R' indicating direction of robot i
5 * @param d - Distance each robot moves
6 * @returns The sum of distances between all pairs modulo 10^9 + 7
7 */
8function sumDistance(nums: number[], s: string, d: number): number {
9    const n: number = nums.length;
10  
11    // Update each robot's position based on direction and distance
12    // Move left if 'L' (subtract d), move right if 'R' (add d)
13    for (let i: number = 0; i < n; ++i) {
14        nums[i] += s[i] === 'L' ? -d : d;
15    }
16  
17    // Sort the final positions in ascending order
18    nums.sort((a: number, b: number) => a - b);
19  
20    // Calculate sum of pairwise distances using mathematical optimization
21    let totalDistance: number = 0;
22    let prefixSum: number = 0;
23    const MOD: number = 1e9 + 7;
24  
25    // For each position, calculate its contribution to total distance
26    // Position i contributes: i * nums[i] - sum of all previous positions
27    for (let i: number = 0; i < n; ++i) {
28        totalDistance = (totalDistance + i * nums[i] - prefixSum) % MOD;
29        prefixSum += nums[i];
30    }
31  
32    return totalDistance;
33}
34

Time and Space Complexity

The time complexity of this algorithm is O(n log n), where n is the number of robots (length of the nums array).

Breaking down the operations:

  • The first loop iterates through the string s and updates positions in nums: O(n)
  • Sorting the nums array: O(n log n)
  • The second loop calculates the sum of distances: O(n)
  • Overall time complexity: O(n) + O(n log n) + O(n) = O(n log n)

The space complexity is O(n).

The analysis:

  • The sorting operation typically requires O(log n) space for the recursion stack in algorithms like quicksort, or O(n) space for algorithms like mergesort
  • Python's built-in sort() uses Timsort, which requires O(n) auxiliary space in the worst case
  • No additional data structures are created besides temporary variables
  • Overall space complexity: O(n)

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

Common Pitfalls

1. Integer Overflow Before Applying Modulo

The Pitfall: The most common mistake is applying the modulo operation only at the very end. When calculating the sum of distances, intermediate values can exceed the maximum integer size in languages with fixed integer limits, causing overflow errors. Even in Python (which handles arbitrary precision integers), this can lead to performance issues with very large numbers.

Example of the Problem:

# WRONG - can cause overflow in other languages or performance issues
total_distance = 0
for i, position in enumerate(nums):
    total_distance += i * position - prefix_sum
    prefix_sum += position
return total_distance % MOD  # Only applying modulo at the end

The Solution: Apply modulo operation during the accumulation to keep numbers manageable:

# CORRECT - apply modulo during calculation
total_distance = 0
prefix_sum = 0
for i, position in enumerate(nums):
    total_distance = (total_distance + i * position - prefix_sum) % MOD
    prefix_sum += position
return total_distance

2. Forgetting to Handle Negative Modulo Results

The Pitfall: When subtracting prefix_sum from the calculation, the intermediate result can become negative. In some programming languages, taking modulo of a negative number can produce unexpected results.

Example of the Problem:

# Potential issue with negative intermediate values
total_distance += i * position - prefix_sum  # This can be negative

The Solution: Ensure the result is always positive by adding MOD before taking modulo:

# SAFE - handles negative values correctly
total_distance = (total_distance + i * position - prefix_sum + MOD) % MOD

Or alternatively:

# Also safe - separate addition and subtraction
total_distance = (total_distance + i * position) % MOD
total_distance = (total_distance - prefix_sum + MOD) % MOD

3. Modifying Input Array Without Consideration

The Pitfall: The solution modifies the nums array in-place when updating positions. This might not be acceptable if the original array needs to be preserved for other purposes or if the problem explicitly states not to modify the input.

The Solution: Create a copy of the array before modifications:

def sumDistance(self, nums: List[int], s: str, d: int) -> int:
    MOD = 10**9 + 7
  
    # Create a copy to preserve original array
    positions = nums.copy()  # or positions = list(nums)
  
    # Now modify the copy instead
    for i, direction in enumerate(s):
        if direction == "R":
            positions[i] += d
        else:
            positions[i] -= d
  
    positions.sort()
    # ... rest of the solution
Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

What's the output of running the following function using input 56?

1KEYBOARD = {
2    '2': 'abc',
3    '3': 'def',
4    '4': 'ghi',
5    '5': 'jkl',
6    '6': 'mno',
7    '7': 'pqrs',
8    '8': 'tuv',
9    '9': 'wxyz',
10}
11
12def letter_combinations_of_phone_number(digits):
13    def dfs(path, res):
14        if len(path) == len(digits):
15            res.append(''.join(path))
16            return
17
18        next_number = digits[len(path)]
19        for letter in KEYBOARD[next_number]:
20            path.append(letter)
21            dfs(path, res)
22            path.pop()
23
24    res = []
25    dfs([], res)
26    return res
27
1private static final Map<Character, char[]> KEYBOARD = Map.of(
2    '2', "abc".toCharArray(),
3    '3', "def".toCharArray(),
4    '4', "ghi".toCharArray(),
5    '5', "jkl".toCharArray(),
6    '6', "mno".toCharArray(),
7    '7', "pqrs".toCharArray(),
8    '8', "tuv".toCharArray(),
9    '9', "wxyz".toCharArray()
10);
11
12public static List<String> letterCombinationsOfPhoneNumber(String digits) {
13    List<String> res = new ArrayList<>();
14    dfs(new StringBuilder(), res, digits.toCharArray());
15    return res;
16}
17
18private static void dfs(StringBuilder path, List<String> res, char[] digits) {
19    if (path.length() == digits.length) {
20        res.add(path.toString());
21        return;
22    }
23    char next_digit = digits[path.length()];
24    for (char letter : KEYBOARD.get(next_digit)) {
25        path.append(letter);
26        dfs(path, res, digits);
27        path.deleteCharAt(path.length() - 1);
28    }
29}
30
1const KEYBOARD = {
2    '2': 'abc',
3    '3': 'def',
4    '4': 'ghi',
5    '5': 'jkl',
6    '6': 'mno',
7    '7': 'pqrs',
8    '8': 'tuv',
9    '9': 'wxyz',
10}
11
12function letter_combinations_of_phone_number(digits) {
13    let res = [];
14    dfs(digits, [], res);
15    return res;
16}
17
18function dfs(digits, path, res) {
19    if (path.length === digits.length) {
20        res.push(path.join(''));
21        return;
22    }
23    let next_number = digits.charAt(path.length);
24    for (let letter of KEYBOARD[next_number]) {
25        path.push(letter);
26        dfs(digits, path, res);
27        path.pop();
28    }
29}
30

Recommended Readings

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

Load More