Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1138. Alphabet Board Path - Leetcode Solution

Problem Description

The problem "Alphabet Board Path" presents a special 5x5 board where each cell contains a lowercase English letter from 'a' to 'y', and the letter 'z' is located alone in the next row at position (5, 0). The board looks like this:

    a b c d e
    f g h i j
    k l m n o
    p q r s t
    u v w x y
    z
  

You start at position (0, 0) (which is 'a'). Given a string target consisting of lowercase English letters, you need to generate a path from the starting position to spell out the target string by moving up (U), down (D), left (L), or right (R) on the board. After reaching each letter, you must append a ! to indicate that the letter has been selected. The result should be a single string representing the moves and selections for the entire target.

Constraints:

  • There is always at least one valid path for any given target.
  • You cannot move outside the board at any time.
  • You must not reuse moves for the same letter; each move is for the next letter in target.

Thought Process

To solve this problem, the first instinct might be to simulate every possible path for each letter in target, but that would be highly inefficient. Instead, a better approach is to:

  • Map each letter to its coordinates on the board.
  • For each letter in target, calculate the difference in rows and columns from the current position.
  • Generate the shortest path by moving vertically and horizontally as needed.
  • Pay special attention to the letter 'z', since it's the only letter in its row and moving to/from it requires careful handling to avoid invalid moves.

The main challenge is to avoid stepping outside the board, especially when dealing with 'z'. The solution is to always move vertically before horizontally when moving up to 'z', and horizontally before vertically when moving down from 'z'.

Solution Approach

Let's break down the solution step by step:

  1. Map Letters to Positions:
    • Create a mapping from each letter ('a' to 'z') to its (row, column) position on the board.
  2. Iterate Through the Target String:
    • Start from position (0, 0).
    • For each letter in target, find its target coordinates.
  3. Generate Moves:
    • Calculate the difference in rows (dr) and columns (dc) between the current and target positions.
    • To avoid invalid positions (especially with 'z'), if moving upwards or to 'z', move vertically first, then horizontally.
    • Otherwise, move horizontally first, then vertically.
    • For each unit of difference, add the corresponding move character ('U', 'D', 'L', 'R') to the path.
  4. Select the Letter:
    • After reaching the target letter, append '!' to the path.
  5. Update Current Position:
    • Set the current position to the new letter's position and repeat for the next letter.

This approach ensures that you always generate a valid and efficient path for any target.

Example Walkthrough

Let's consider the input target = "leet".

  1. Start at 'a' (0, 0).
  2. The first letter is 'l' (2, 1):
    • Move down 2 times: "DD"
    • Move right 1 time: "R"
    • Select: "!"
    • Path so far: "DDR!"
  3. Next letter is 'e' (0, 4):
    • Move up 2 times: "UU"
    • Move right 3 times: "RRR"
    • Select: "!"
    • Path so far: "DDR!UURRR!"
  4. Next letter is 'e' (again at 0, 4):
    • Already at the correct position, just select: "!"
    • Path so far: "DDR!UURRR!!"
  5. Next letter is 't' (4, 4):
    • Move down 4 times: "DDDD"
    • Select: "!"
    • Final path: "DDR!UURRR!!DDDD!"

The final output is DDR!UURRR!!DDDD!.

Time and Space Complexity

Brute-force approach: If you tried all possible paths, the time complexity would be exponential in the length of target and the size of the board, which is infeasible.

Optimized approach:

  • Time Complexity: O(N), where N is the length of target. For each letter, you perform a constant number of operations (calculating moves and appending to the result).
  • Space Complexity: O(1) for the mapping (since the board is always 26 letters), plus O(N) for the output string.
This efficiency is achieved by directly computing the shortest path between each pair of consecutive letters.

Summary

The "Alphabet Board Path" problem is elegantly solved by mapping each letter to its board coordinates and calculating the shortest valid path between consecutive letters in the target string. By handling the special case of 'z' and always moving in a safe order, we avoid invalid moves and ensure correctness. The solution is efficient, intuitive, and leverages simple coordinate arithmetic to generate the required path.

Code Implementation

class Solution:
    def alphabetBoardPath(self, target: str) -> str:
        pos = {c: (divmod(ord(c) - ord('a'), 5)) for c in 'abcdefghijklmnopqrstuvwxyz'}
        res = []
        x, y = 0, 0
        for ch in target:
            nx, ny = pos[ch]
            # Move up or left first to avoid invalid moves (especially for 'z')
            if nx < x:
                res.append('U' * (x - nx))
            if ny < y:
                res.append('L' * (y - ny))
            if nx > x:
                res.append('D' * (nx - x))
            if ny > y:
                res.append('R' * (ny - y))
            res.append('!')
            x, y = nx, ny
        return ''.join(res)
      
class Solution {
public:
    string alphabetBoardPath(string target) {
        vector<pair<int, int>> pos(26);
        for (int i = 0; i < 26; ++i) {
            pos[i] = {i / 5, i % 5};
        }
        int x = 0, y = 0;
        string res;
        for (char ch : target) {
            int nx = pos[ch - 'a'].first, ny = pos[ch - 'a'].second;
            // Move up or left first to avoid invalid moves (especially for 'z')
            if (nx < x) res.append(x - nx, 'U');
            if (ny < y) res.append(y - ny, 'L');
            if (nx > x) res.append(nx - x, 'D');
            if (ny > y) res.append(ny - y, 'R');
            res += '!';
            x = nx; y = ny;
        }
        return res;
    }
};
      
class Solution {
    public String alphabetBoardPath(String target) {
        int[][] pos = new int[26][2];
        for (int i = 0; i < 26; i++) {
            pos[i][0] = i / 5;
            pos[i][1] = i % 5;
        }
        int x = 0, y = 0;
        StringBuilder res = new StringBuilder();
        for (char ch : target.toCharArray()) {
            int nx = pos[ch - 'a'][0], ny = pos[ch - 'a'][1];
            // Move up or left first to avoid invalid moves (especially for 'z')
            if (nx < x) for (int i = 0; i < x - nx; i++) res.append('U');
            if (ny < y) for (int i = 0; i < y - ny; i++) res.append('L');
            if (nx > x) for (int i = 0; i < nx - x; i++) res.append('D');
            if (ny > y) for (int i = 0; i < ny - y; i++) res.append('R');
            res.append('!');
            x = nx; y = ny;
        }
        return res.toString();
    }
}
      
var alphabetBoardPath = function(target) {
    const pos = {};
    for (let i = 0; i < 26; i++) {
        pos[String.fromCharCode(97 + i)] = [Math.floor(i / 5), i % 5];
    }
    let res = '';
    let x = 0, y = 0;
    for (let ch of target) {
        let [nx, ny] = pos[ch];
        // Move up or left first to avoid invalid moves (especially for 'z')
        if (nx < x) res += 'U'.repeat(x - nx);
        if (ny < y) res += 'L'.repeat(y - ny);
        if (nx > x) res += 'D'.repeat(nx - x);
        if (ny > y) res += 'R'.repeat(ny - y);
        res += '!';
        x = nx; y = ny;
    }
    return res;
};