2120. Execution of All Suffix Instructions Staying in a Grid
Problem Description
You have an n x n
grid where cells are indexed from (0, 0)
at the top-left to (n-1, n-1)
at the bottom-right. A robot starts at position startPos = [startRow, startCol]
.
You're given a string s
of length m
containing movement instructions:
'L'
- move left (decrease column by 1)'R'
- move right (increase column by 1)'U'
- move up (decrease row by 1)'D'
- move down (increase row by 1)
The key twist is that the robot can start executing from any position i
in the instruction string (not just from the beginning). When starting from position i
, the robot:
- Executes instructions sequentially from index
i
to the end of strings
- Stops if the next instruction would move it outside the grid boundaries
- Stops when there are no more instructions left
Your task is to return an array answer
of length m
, where answer[i]
represents the total number of instructions the robot can successfully execute when starting from the i-th
instruction in string s
.
For example, if n = 3
, startPos = [0, 1]
, and s = "RRDDLU"
:
- Starting from index 0: The robot might execute several moves before hitting a boundary or finishing all instructions
- Starting from index 1: The robot begins with the second 'R' and continues from there
- And so on for each starting position
The solution simulates each possible starting position by:
- Iterating through each index
i
as a potential starting point - From position
i
, attempting to execute each subsequent instruction - Checking if each move keeps the robot within the grid bounds
[0, n-1]
- Counting valid moves until either hitting a boundary or reaching the end of instructions
Intuition
The problem asks us to find how many instructions can be executed from each possible starting position in the instruction string. This naturally suggests a simulation approach - we need to actually try starting from each position and see how far we can go.
Think of it like testing different "entry points" into a sequence of directions. If you have a path like "go right, right, down, down, left, up", you want to know: what if I skip the first instruction and start from the second? What if I start from the third? For each scenario, we need to count how many steps we can take before hitting a wall.
The brute force approach is actually the most straightforward here: for each possible starting index i
in the string, we simulate the robot's movement from that point onwards. We keep track of the robot's current position and try to execute each instruction in sequence. If a move would take the robot outside the grid (row or column becomes negative or >= n), we stop counting. Otherwise, we update the position and increment our counter.
The key insight is that we need to:
- Reset the robot to its original starting position for each new starting index
- Map each direction character to its corresponding coordinate change:
'L'
means[0, -1]
,'R'
means[0, 1]
,'U'
means[-1, 0]
, and'D'
means[1, 0]
- Before making each move, check if the new position would be valid (within bounds)
Since we need to compute this for all m
starting positions and for each position we might execute up to m
instructions in the worst case, the time complexity is O(m²)
. This is acceptable given typical constraints, and the simulation approach gives us exactly what we need without any complex preprocessing or optimization.
Solution Approach
The solution implements a straightforward simulation approach using nested loops to test each possible starting position.
Data Structure Setup:
We use a dictionary mp
to map each direction character to its corresponding movement vector:
mp = {"L": [0, -1], "R": [0, 1], "U": [-1, 0], "D": [1, 0]}
This makes it easy to translate instructions into coordinate changes.
Main Algorithm:
-
Outer Loop - Iterate through each possible starting index
i
from0
tom-1
:for i in range(m):
-
Initialize for Each Starting Position:
- Reset the robot's position to the original
startPos
- Initialize a counter
t = 0
to track valid moves
x, y = startPos t = 0
- Reset the robot's position to the original
-
Inner Loop - Simulate execution from index
i
to the end:for j in range(i, m):
-
Process Each Instruction:
- Get the movement vector for the current instruction:
a, b = mp[s[j]]
- Check if the move would keep the robot in bounds:
0 <= x + a < n and 0 <= y + b < n
- If valid, update position and increment counter:
x, y, t = x + a, y + b, t + 1
- If invalid, break out of the inner loop
- Get the movement vector for the current instruction:
-
Store Result: After simulating from position
i
, append the countt
to the answer array.
Boundary Checking:
The condition 0 <= x + a < n and 0 <= y + b < n
ensures both:
- The robot doesn't go below row/column 0 (left or top boundary)
- The robot doesn't reach or exceed row/column n (right or bottom boundary)
Time Complexity: O(m²)
where m is the length of the instruction string, as we potentially examine all remaining instructions for each starting position.
Space Complexity: O(1)
excluding the output array, as we only use a few variables to track position and counts.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example to illustrate the solution approach.
Given:
- Grid size:
n = 3
(3×3 grid) - Starting position:
startPos = [0, 1]
(top row, middle column) - Instructions:
s = "RRDDLU"
Grid visualization:
[0,0] [0,1] [0,2] [1,0] [1,1] [1,2] [2,0] [2,1] [2,2]
Direction mapping:
- 'R' → [0, 1] (move right)
- 'D' → [1, 0] (move down)
- 'L' → [0, -1] (move left)
- 'U' → [-1, 0] (move up)
Simulating each starting position:
Starting from index 0 (s = "RRDDLU"):
- Position: [0,1], instruction 'R' → [0,2] ✓ (valid, count = 1)
- Position: [0,2], instruction 'R' → [0,3] ✗ (out of bounds, stop)
- Result: 1
Starting from index 1 (s = "RDDLU"):
- Position: [0,1], instruction 'R' → [0,2] ✓ (count = 1)
- Position: [0,2], instruction 'D' → [1,2] ✓ (count = 2)
- Position: [1,2], instruction 'D' → [2,2] ✓ (count = 3)
- Position: [2,2], instruction 'L' → [2,1] ✓ (count = 4)
- Position: [2,1], instruction 'U' → [1,1] ✓ (count = 5)
- Result: 5
Starting from index 2 (s = "DDLU"):
- Position: [0,1], instruction 'D' → [1,1] ✓ (count = 1)
- Position: [1,1], instruction 'D' → [2,1] ✓ (count = 2)
- Position: [2,1], instruction 'L' → [2,0] ✓ (count = 3)
- Position: [2,0], instruction 'U' → [1,0] ✓ (count = 4)
- Result: 4
Starting from index 3 (s = "DLU"):
- Position: [0,1], instruction 'D' → [1,1] ✓ (count = 1)
- Position: [1,1], instruction 'L' → [1,0] ✓ (count = 2)
- Position: [1,0], instruction 'U' → [0,0] ✓ (count = 3)
- Result: 3
Starting from index 4 (s = "LU"):
- Position: [0,1], instruction 'L' → [0,0] ✓ (count = 1)
- Position: [0,0], instruction 'U' → [-1,0] ✗ (out of bounds, stop)
- Result: 1
Starting from index 5 (s = "U"):
- Position: [0,1], instruction 'U' → [-1,1] ✗ (out of bounds, stop)
- Result: 0
Final answer: [1, 5, 4, 3, 1, 0]
The algorithm correctly simulates each starting position, tracking how many valid moves can be made before either hitting a boundary or running out of instructions.
Solution Implementation
1class Solution:
2 def executeInstructions(self, n: int, startPos: List[int], s: str) -> List[int]:
3 """
4 For each starting index in string s, count how many valid moves can be executed
5 within an n x n grid starting from startPos.
6
7 Args:
8 n: Size of the square grid (n x n)
9 startPos: Starting position [row, col] in the grid
10 s: String of instructions where L=left, R=right, U=up, D=down
11
12 Returns:
13 List where result[i] = number of valid moves starting from instruction i
14 """
15 result = []
16 num_instructions = len(s)
17
18 # Direction mappings: instruction -> [row_delta, col_delta]
19 directions = {
20 "L": [0, -1], # Move left: column decreases
21 "R": [0, 1], # Move right: column increases
22 "U": [-1, 0], # Move up: row decreases
23 "D": [1, 0] # Move down: row increases
24 }
25
26 # Try starting from each position in the instruction string
27 for start_idx in range(num_instructions):
28 current_row, current_col = startPos
29 valid_moves_count = 0
30
31 # Execute instructions starting from start_idx
32 for instruction_idx in range(start_idx, num_instructions):
33 # Get the direction deltas for current instruction
34 row_delta, col_delta = directions[s[instruction_idx]]
35
36 # Check if the next position would be within grid bounds
37 next_row = current_row + row_delta
38 next_col = current_col + col_delta
39
40 if 0 <= next_row < n and 0 <= next_col < n:
41 # Valid move: update position and increment counter
42 current_row = next_row
43 current_col = next_col
44 valid_moves_count += 1
45 else:
46 # Invalid move: stop executing further instructions
47 break
48
49 result.append(valid_moves_count)
50
51 return result
52
1class Solution {
2 public int[] executeInstructions(int n, int[] startPos, String s) {
3 int instructionCount = s.length();
4 int[] result = new int[instructionCount];
5
6 // Create a mapping of directions to their corresponding row/column changes
7 Map<Character, int[]> directionMap = new HashMap<>(4);
8 directionMap.put('L', new int[] {0, -1}); // Left: no row change, column -1
9 directionMap.put('R', new int[] {0, 1}); // Right: no row change, column +1
10 directionMap.put('U', new int[] {-1, 0}); // Up: row -1, no column change
11 directionMap.put('D', new int[] {1, 0}); // Down: row +1, no column change
12
13 // For each starting instruction index, calculate maximum executable instructions
14 for (int startIndex = 0; startIndex < instructionCount; startIndex++) {
15 int currentRow = startPos[0];
16 int currentCol = startPos[1];
17 int executedCount = 0;
18
19 // Try to execute instructions starting from startIndex
20 for (int currentIndex = startIndex; currentIndex < instructionCount; currentIndex++) {
21 char instruction = s.charAt(currentIndex);
22 int[] movement = directionMap.get(instruction);
23 int rowChange = movement[0];
24 int colChange = movement[1];
25
26 // Calculate new position
27 int newRow = currentRow + rowChange;
28 int newCol = currentCol + colChange;
29
30 // Check if new position is within grid boundaries
31 if (newRow >= 0 && newRow < n && newCol >= 0 && newCol < n) {
32 // Valid move: update position and increment counter
33 currentRow = newRow;
34 currentCol = newCol;
35 executedCount++;
36 } else {
37 // Out of bounds: stop executing instructions
38 break;
39 }
40 }
41
42 // Store the count of executed instructions for this starting index
43 result[startIndex] = executedCount;
44 }
45
46 return result;
47 }
48}
49
1class Solution {
2public:
3 vector<int> executeInstructions(int n, vector<int>& startPos, string s) {
4 int instructionCount = s.size();
5 vector<int> result(instructionCount);
6
7 // Map each direction character to its corresponding row and column deltas
8 unordered_map<char, vector<int>> directionMap;
9 directionMap['L'] = {0, -1}; // Left: no row change, column decreases
10 directionMap['R'] = {0, 1}; // Right: no row change, column increases
11 directionMap['U'] = {-1, 0}; // Up: row decreases, no column change
12 directionMap['D'] = {1, 0}; // Down: row increases, no column change
13
14 // For each starting instruction index, calculate maximum valid moves
15 for (int startIndex = 0; startIndex < instructionCount; ++startIndex) {
16 int currentRow = startPos[0];
17 int currentCol = startPos[1];
18 int validMoves = 0;
19
20 // Try to execute instructions starting from startIndex
21 for (int instructionIndex = startIndex; instructionIndex < instructionCount; ++instructionIndex) {
22 // Get the direction deltas for current instruction
23 int rowDelta = directionMap[s[instructionIndex]][0];
24 int colDelta = directionMap[s[instructionIndex]][1];
25
26 // Check if the next position would be within grid bounds
27 int nextRow = currentRow + rowDelta;
28 int nextCol = currentCol + colDelta;
29
30 if (nextRow >= 0 && nextRow < n && nextCol >= 0 && nextCol < n) {
31 // Valid move: update position and increment counter
32 currentRow = nextRow;
33 currentCol = nextCol;
34 ++validMoves;
35 } else {
36 // Out of bounds: stop executing instructions
37 break;
38 }
39 }
40
41 result[startIndex] = validMoves;
42 }
43
44 return result;
45 }
46};
47
1/**
2 * Executes movement instructions on a grid and returns the maximum number of instructions
3 * that can be executed starting from each position without going out of bounds.
4 *
5 * @param n - The size of the n x n grid
6 * @param startPos - The starting position [row, column] on the grid
7 * @param s - String of instructions where 'U'=up, 'D'=down, 'L'=left, 'R'=right
8 * @returns Array where ans[i] represents the maximum number of consecutive instructions
9 * that can be executed starting from instruction i
10 */
11function executeInstructions(n: number, startPos: number[], s: string): number[] {
12 const instructionCount: number = s.length;
13 const result: number[] = new Array(instructionCount);
14
15 // Try starting from each instruction position
16 for (let startIndex: number = 0; startIndex < instructionCount; startIndex++) {
17 // Initialize current position from the starting position
18 let currentRow: number = startPos[0];
19 let currentCol: number = startPos[1];
20 let endIndex: number;
21
22 // Execute instructions sequentially from startIndex
23 for (endIndex = startIndex; endIndex < instructionCount; endIndex++) {
24 const instruction: string = s[endIndex];
25
26 // Update position based on instruction
27 if (instruction === 'U') {
28 currentRow--; // Move up (decrease row)
29 } else if (instruction === 'D') {
30 currentRow++; // Move down (increase row)
31 } else if (instruction === 'L') {
32 currentCol--; // Move left (decrease column)
33 } else {
34 currentCol++; // Move right (increase column)
35 }
36
37 // Check if the new position is out of bounds
38 if (currentRow === -1 || currentRow === n || currentCol === -1 || currentCol === n) {
39 break; // Stop execution if we go out of bounds
40 }
41 }
42
43 // Store the count of successfully executed instructions
44 result[startIndex] = endIndex - startIndex;
45 }
46
47 return result;
48}
49
Time and Space Complexity
Time Complexity: O(m²)
The code contains two nested loops:
- The outer loop runs
m
times wherem = len(s)
(the length of the instruction string) - For each iteration of the outer loop, the inner loop can run up to
m - i
times in the worst case - In the worst case where all moves are valid, the total number of operations is:
m + (m-1) + (m-2) + ... + 1 = m(m+1)/2
- This simplifies to
O(m²)
Space Complexity: O(1)
excluding output, O(m)
including output
- The code uses a constant amount of extra space for variables:
x
,y
,t
,a
,b
, and the dictionarymp
- The dictionary
mp
has a fixed size of 4 entries regardless of input size - The answer list
ans
storesm
elements, but this is required for the output - If we exclude the space needed for the output, the space complexity is
O(1)
- If we include the output space, the space complexity is
O(m)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Modifying the Original Starting Position
A critical mistake is accidentally modifying the original startPos
instead of creating a copy for each simulation. This would cause incorrect results for subsequent starting indices.
Incorrect approach:
for i in range(m):
position = startPos # WRONG: This creates a reference, not a copy!
position[0] += row_delta # This modifies the original startPos!
Correct approach:
for i in range(m):
current_row, current_col = startPos # Creates new variables with values
# OR
position = startPos.copy() # Creates an actual copy of the list
2. Off-by-One Errors in Boundary Checking
Confusing inclusive vs exclusive bounds is a common mistake. The grid has valid indices from 0 to n-1, not 0 to n.
Incorrect boundary check:
if 0 <= next_row <= n and 0 <= next_col <= n: # WRONG: allows position n
Correct boundary check:
if 0 <= next_row < n and 0 <= next_col < n: # Correct: excludes position n
3. Updating Position Before Validation
Moving the robot before checking if the move is valid leads to incorrect positions and counts.
Incorrect order:
for j in range(i, m):
row_delta, col_delta = directions[s[j]]
current_row += row_delta # WRONG: Updates before checking validity
current_col += col_delta
if 0 <= current_row < n and 0 <= current_col < n:
valid_moves_count += 1
else:
break # Position is already corrupted!
Correct order:
for j in range(i, m):
row_delta, col_delta = directions[s[j]]
next_row = current_row + row_delta # Calculate without updating
next_col = current_col + col_delta
if 0 <= next_row < n and 0 <= next_col < n:
current_row = next_row # Update only after validation
current_col = next_col
valid_moves_count += 1
else:
break
4. Incorrect Direction Mappings
Mixing up the coordinate system (row, column) with direction meanings can cause the robot to move incorrectly.
Common confusion:
- Remember that row index increases downward (U decreases row, D increases row)
- Column index increases rightward (L decreases column, R increases column)
Incorrect mapping:
directions = { "U": [1, 0], # WRONG: Up should decrease row, not increase "D": [-1, 0], # WRONG: Down should increase row, not decrease }
5. Not Breaking the Loop After Invalid Move
Forgetting to break when encountering an invalid move would skip that instruction but continue with others, which violates the problem requirements.
Incorrect implementation:
for j in range(i, m):
if valid_move:
# execute move
valid_moves_count += 1
# WRONG: Missing break, continues to next instruction even after invalid move
Correct implementation:
for j in range(i, m):
if valid_move:
# execute move
valid_moves_count += 1
else:
break # Stop immediately upon encountering invalid move
Which of the following uses divide and conquer strategy?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!