2120. Execution of All Suffix Instructions Staying in a Grid

MediumStringSimulation
Leetcode Link

Problem Description

In this problem, we are given a scenario where we have an n x n grid, and a robot is situated at a starting position on this grid. The robot's position is defined by the startPos, which is an array of two integers representing the row and column. Additionally, we are provided with a string s of length m, representing a sequence of instructions ('L', 'R', 'U', 'D') that tell the robot how to move within the grid.

The task is to find out, for each instruction starting from the ith position in the string s, how many instructions the robot can execute before one of two conditions occurs:

  1. The robot is instructed to move off the grid (out of bounds).
  2. The robot reaches the end of the instruction string without moving off the grid.

The final output should be an array answer where each element answer[i] represents the number of instructions the robot can execute when starting from the ith instruction in s.

Intuition

The solution to this problem lies in simulating the path of the robot's movement on the grid from each possible starting point in the instruction string s.

For each instruction starting point i, we simulate the robot's movement by iterating over instructions from s[i] to s[m - 1] and tracking its current position. We use a dictionary mp to translate the instruction characters ('L', 'R', 'U', 'D') into corresponding row and column movements.

With each step, we:

  • Check if the next move stays within the grid boundaries.
  • If it does, we update the robot's position and increment the counter for executed instructions.
  • If the next move would take the robot out of bounds, we stop and consider the number of executed instructions as the result for the starting point i.

We repeat this process for every instruction as a potential starting point, obtaining the total number of instructions the robot can execute for each case. These counts are added to the ans list in the order of instruction starting points.

The approach relies on a brute-force simulation for each starting point, which simplifies the implementation and ensures correctness by considering every possible execution path from every starting point.

Not Sure What to Study? Take the 2-min Quiz to Find Your Missing Piece:

What are the most two important steps in writing a depth first search function? (Select 2)

Solution Approach

The provided solution approach utilizes the simulation strategy, where we follow the robot's instructions from each possible start point in the instruction string s until it either moves off the grid or runs out of instructions. Here are the steps and the constructs used in the implementation:

Algorithm:

  1. Initialize an empty list (ans) to collect the number of executable instructions for each starting point.

  2. Create a mapping (mp) which translates instruction characters ('L', 'R', 'U', 'D') to their corresponding movements on the grid as row (x) and column (y) changes.

  3. Loop through each instruction i as a start point in the instruction string s. For each start point:

    a. Initial Position: Set the robot's current position (x, y) to the starting position (startPos).

    b. Execution Counter (t): Initialize a counter to keep track of how many instructions have been executed from that starting point.

  4. Iterate over the instructions from the current start point i to the end of the string. For each instruction j:

    a. Retrieve the corresponding movement from the mp dictionary.

    b. Check if the movement is within grid bounds:

    • If the resulting position (x + a, y + b) is within the grid (0 <=x < n and 0 <= y < n), apply the movement to the current position and increment the t counter.
    • If the move would go off-grid, break out of the loop since the robot cannot execute this instruction.

    c. After the loop, append the value of t (the number of instructions executed) to the ans list.

  5. Once we have simulated starting from each instruction and collected the executable instruction counts, return ans.

Data Structures:

  • List: To store the final count of executable instructions (ans) and the starting position (startPos).

  • Dictionary: To translate instructions into actual x and y movements (mp).

Patterns:

  • Simulation: This problem is directly addressed through simulatory execution of instructions, mimicking the exact behavior we are trying to query, thus avoiding the need for more complex algorithms or optimizations.

By using this brute-force simulation, the solution effectively handles any given n x n grid and instruction set s, ensuring a correct and complete answer.

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

Which of the following is a min heap?

Example Walkthrough

Let's walk through a small example to illustrate the solution approach described above.

Suppose we have a grid of size 3 x 3, and the robot starts at startPos = [1,1] (the center of the grid). Imagine our string of instructions, s, is "RUL". We want to find out how many instructions the robot can execute starting at each character of the instruction string before moving off the grid.

Now let's simulate according to the algorithm:

  1. Initialize an empty list (ans) to store results.
  2. Create a mapping (mp) of {'L': (0, -1), 'R': (0, 1), 'U': (-1, 0), 'D': (1, 0)} to represent movements.
  3. Loop through each i in s — here, i will take values 0 for 'R', 1 for 'U', and 2 for 'L'.

For i = 0 ('R'):

  • Start from startPos (1,1).
  • Move right (0, 1) to (1,2). Grid boundary not exceeded, increment t to 1.
  • Next is 'U', move up (-1, 0) to (0,2). Grid boundary not exceeded, increment t to 2.
  • Last is 'L', move left (0, -1) to (0,1). Grid boundary not exceeded, increment t to 3.
  • Add t = 3 to ans.

For i = 1 ('U'):

  • Start from startPos (1, 1).
  • Move up (-1, 0) to (0, 1). Grid boundary not exceeded, increment t to 1.
  • Next is 'L', move left (0, -1) to (0,0). Grid boundary not exceeded, increment t to 2.
  • There are no more instructions, so add t = 2 to ans.

For i = 2 ('L'):

  • Start from startPos (1, 1).
  • Move left (0, -1) to (1,0). Grid boundary not exceeded, increment t to 1.
  • No more instructions, so add t = 1 to ans.

After the loop, we have: ans = [3, 2, 1].

  1. Return ans, which gives us the count of executable instructions for the robot starting from each position in the string s.

So, the output would be [3, 2, 1] for our example, indicating that starting from the first instruction 'R', the robot can execute 3 instructions; from 'U', 2 instructions; and from 'L', 1 instruction before going off the grid or running out of instructions.

Solution Implementation

1from typing import List
2
3class Solution:
4    def execute_instructions(self, n: int, start_pos: List[int], instructions: str) -> List[int]:
5        # Initialize the result list
6        results = []
7      
8        # Length of the instruction string
9        num_instructions = len(instructions)
10      
11        # Mapping for each instruction to its corresponding row and column changes
12        direction_map = {"L": [0, -1], "R": [0, 1], "U": [-1, 0], "D": [1, 0]}
13      
14        # Iterate over each instruction starting position
15        for i in range(num_instructions):
16            # Starting position for this sequence of instructions
17            x, y = start_pos
18          
19            # Count of valid moves from the current starting instruction
20            valid_moves = 0
21          
22            # Execute instructions starting at index i and onwards
23            for j in range(i, num_instructions):
24                # Get the row and column changes for the current instruction
25                row_change, col_change = direction_map[instructions[j]]
26              
27                # Update the current position based on the instruction
28                x += row_change
29                y += col_change
30              
31                # Check if the new position is still within bounds
32                if 0 <= x < n and 0 <= y < n:
33                    # Increase the count of valid moves
34                    valid_moves += 1
35                else:
36                    # If out of bounds, no more valid moves, break the loop
37                    break
38          
39            # Add the count of valid moves for this sequence to the results list
40            results.append(valid_moves)
41      
42        # Return the list of valid moves for each starting point
43        return results
44
1class Solution {
2    public int[] executeInstructions(int n, int[] startPos, String instructions) {
3        int instructionsLength = instructions.length(); // Length of the instructions string
4        int[] results = new int[instructionsLength]; // Array to store the result for each position
5        // A map to associate directions with their respective 2D coordinate changes
6        Map<Character, int[]> directionMap = new HashMap<>(4); 
7        directionMap.put('L', new int[] {0, -1});// Left move
8        directionMap.put('R', new int[] {0, 1}); // Right move
9        directionMap.put('U', new int[] {-1, 0});// Up move
10        directionMap.put('D', new int[] {1, 0}); // Down move
11
12        // Loop through each position in the instructions as a starting point
13        for (int start = 0; start < instructionsLength; ++start) {
14            int x = startPos[0], y = startPos[1]; // Current position to check from
15            int validMoves = 0; // Counter to count the valid moves from the current starting position
16
17            // Loop through the instructions starting from the current position
18            for (int current = start; current < instructionsLength; ++current) {
19                char currentInstruction = instructions.charAt(current); // Get the current instruction
20                // Retrieve the 2D coordinate change for the current direction
21                int deltaX = directionMap.get(currentInstruction)[0], deltaY = directionMap.get(currentInstruction)[1]; 
22              
23                // Check if the move within the bounds of the grid
24                if (0 <= x + deltaX && x + deltaX < n && 0 <= y + deltaY && y + deltaY < n) {
25                    x += deltaX; // Update the current x-coordinate
26                    y += deltaY; // Update the current y-coordinate
27                    ++validMoves; // Increment the valid move counter
28                } else {
29                    // A move took us out of the grid bounds, stop checking further instructions
30                    break;
31                }
32            }
33            results[start] = validMoves; // Store the valid moves for this start position
34        }
35        return results; // Return the array containing valid moves for each start position
36    }
37}
38
1#include <vector>
2#include <string>
3#include <unordered_map>
4using namespace std;
5
6class Solution {
7public:
8    vector<int> executeInstructions(int n, vector<int>& startPos, string s) {
9        int instructionCount = s.size(); // number of instructions to execute
10        vector<int> answer(instructionCount); // vector to hold the number of valid instructions for each index
11        unordered_map<char, vector<int>> directionMap; // map to hold the direction vectors
12        // Direction vectors for L, R, U, D
13        directionMap['L'] = {0, -1};
14        directionMap['R'] = {0, 1};
15        directionMap['U'] = {-1, 0};
16        directionMap['D'] = {1, 0};
17
18        // Iterate over each instruction to see how many steps we can take starting from that instruction
19        for (int i = 0; i < instructionCount; ++i) {
20            int posX = startPos[0]; // current X position
21            int posY = startPos[1]; // current Y position
22            int validSteps = 0; // counter for the number of valid steps we can take for current instruction
23
24            // Starting from the i-th instruction, execute instructions until out of bounds
25            for (int j = i; j < instructionCount; ++j) {
26                // Get the move's direction vector (dx, dy) based on the j-th instruction
27                int dx = directionMap[s[j]][0];
28                int dy = directionMap[s[j]][1];
29              
30                // Check if the new position after the move is within bounds
31                if (0 <= posX + dx && posX + dx < n && 0 <= posY + dy && posY + dy < n) {
32                    // Update position and increment valid step count
33                    posX += dx;
34                    posY += dy;
35                    ++validSteps;
36                } else {
37                    // If the move is out of bounds, break the loop
38                    break;
39                }
40            }
41            // Store the number of valid steps for the i-th starting instruction
42            answer[i] = validSteps;
43        }
44        return answer; // return the vector with the results
45    }
46};
47
1// This function takes a grid size (n), a starting position (startPos),
2// and a string of instructions (s) and calculates the number of valid positions
3// the robot can move to following the instructions starting at each position within the string.
4function executeInstructions(gridSize: number, startPos: number[], instructions: string): number[] {
5    const instructionsLength = instructions.length;
6    const answerArray = new Array(instructionsLength);
7
8    // Loop through each instruction in the string.
9    for (let i = 0; i < instructionsLength; i++) {
10        // Initialize the robot's current position to the starting position.
11        let [currentRow, currentCol] = startPos;
12        let stepCount: number;
13
14        // Iterate over the remaining instructions from the current position.
15        for (stepCount = i; stepCount < instructionsLength; stepCount++) {
16            const currentInstruction = instructions[stepCount];
17
18            // Update the current position based on the instruction.
19            if (currentInstruction === 'U') {
20                currentRow--;
21            } else if (currentInstruction === 'D') {
22                currentRow++;
23            } else if (currentInstruction === 'L') {
24                currentCol--;
25            } else {
26                currentCol++;
27            }
28
29            // If the new position is out of bounds, stop processing further instructions.
30            if (currentRow === -1 || currentRow === gridSize || currentCol === -1 || currentCol === gridSize) {
31                break;
32            }
33        }
34
35        // The number of valid steps taken is the difference between the current
36        // step count and the starting step index for this round of instructions.
37        answerArray[i] = stepCount - i;
38    }
39
40    // Return the array containing the number of valid steps for each starting position in the string.
41    return answerArray;
42}
43
Not Sure What to Study? Take the 2-min Quiz:

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

Time and Space Complexity

The provided Python code implements a function that calculates the number of valid instructions from each starting point in the string s. For calculation, it simulates the movement on an n x n grid based on the instructions given in s.

  • Time Complexity: The outer loop runs m times, where m is the length of the string s. An inner loop runs for each character in s starting at the position i, where i is the index of the outer loop. In the worst case, the inner loop runs m times (when i is 0), and as i increases, the number of iterations decreases. Hence, the number of total operations can be approximated by the sum of the first m natural numbers. This sum is (m*(m+1))/2 which has a complexity of O(m^2).

    The precise sum of operations for the inner loop is:

    1m + (m - 1) + (m - 2) + ... + 2 + 1 = m*(m+1)/2

    Therefore, the time complexity is O(m^2).

  • Space Complexity: The space complexity is determined by the storage used by the algorithm besides the input. Here, the variables used (ans, mp, x, y, a, b, t) do not grow with the size of the input n or the length of the string s; they either are constants or only store single values. The ans list, however, grows with the length of s since an entry is added for each character in s.

    Thus, space complexity is O(m) because the only variable that scales with input size is the ans list, with m being the length of the input string s.

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

Fast Track Your Learning with Our Quick Skills Quiz:

Suppose k is a very large integer(2^64). Which of the following is the largest as n grows to infinity?


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 👨‍🏫