Facebook Pixel

3324. Find the Sequence of Strings Appeared on the Screen

MediumStringSimulation
Leetcode Link

Problem Description

Alice has a special keyboard with only two keys to type a target string:

  • Key 1: Appends the character "a" to the current string
  • Key 2: Changes the last character to its next letter in the alphabet (e.g., "a"→"b", "b"→"c", ..., "z"→"a")

Starting with an empty string, Alice wants to type the target string using the minimum number of key presses. The task is to return all intermediate strings that appear on the screen during this process, in the order they appear.

For example, to type "abc":

  1. Start with empty string ""
  2. Press Key 1: "a" (first character done)
  3. Press Key 1: "aa"
  4. Press Key 2: "ab" (second character done)
  5. Press Key 1: "aba"
  6. Press Key 2: "abb"
  7. Press Key 2: "abc" (target reached)

The solution simulates this process by:

  • For each character c in the target string
  • Starting from the previous string (or empty if first character)
  • Appending "a" and then using Key 2 to cycle through letters until reaching the desired character
  • Recording each intermediate string in the result list

The code iterates through each target character, builds up from 'a' to the required character by cycling through the alphabet, and appends each intermediate result to the answer list.

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

Intuition

The key insight is that we need to build the target string character by character, and for each new character, we have a fixed optimal strategy.

Since we can only append 'a' with Key 1 and then modify it with Key 2, the most efficient way to add any character is to:

  1. First append 'a' to our current string
  2. Then repeatedly press Key 2 to cycle through the alphabet until we reach the desired character

For example, to add 'd' to an existing string, we must:

  • Append 'a' first (Key 1)
  • Transform 'a' → 'b' → 'c' → 'd' (Key 2 three times)

This gives us exactly 4 intermediate strings when adding 'd'.

The crucial observation is that there's no shortcut - we can't skip letters or go backwards in the alphabet. Each character requires us to start from 'a' and increment our way up. This means for a character at position i in the alphabet, we need i+1 intermediate strings (including the initial 'a').

Since we want the minimum key presses, we simply follow this greedy approach for each character in the target. The order is deterministic: for each target character, we generate all strings from appending 'a' up to reaching that character.

This naturally leads to a simulation approach where we process the target character by character, and for each character, we generate all the intermediate strings by cycling from 'a' to that character.

Solution Approach

The simulation approach implements the typing process step by step:

Algorithm Steps:

  1. Initialize an empty result list ans to store all intermediate strings that appear on the screen.

  2. Process each character in the target string one by one:

    • For each character c in target:
      • Get the current string s (empty string if ans is empty, otherwise the last string in ans)
      • Generate all intermediate strings by cycling through the alphabet from 'a' to c
  3. Generate intermediate strings for each character:

    • Start with the current string s
    • Iterate through lowercase letters from 'a' onwards using ascii_lowercase
    • For each letter a in this iteration:
      • Create new string t = s + a (append the letter to current string)
      • Add t to the result list
      • If a equals the target character c, stop iterating

Implementation Details:

class Solution:
    def stringSequence(self, target: str) -> List[str]:
        ans = []
        for c in target:
            s = ans[-1] if ans else ""  # Get current string or empty
            for a in ascii_lowercase:    # Iterate 'a' to 'z'
                t = s + a                # Append current letter
                ans.append(t)            # Record intermediate string
                if a == c:               # Stop when target char reached
                    break
        return ans

Example Walkthrough for target = "ab":

  • Start with ans = []
  • Process 'a': s = "", iterate 'a', create "a", append to ans = ["a"], stop
  • Process 'b': s = "a", iterate 'a' then 'b', create "aa" then "ab", append both, ans = ["a", "aa", "ab"]

The time complexity is O(n × 26) where n is the length of target (worst case when all characters are 'z'), and space complexity is O(m) where m is the total number of intermediate strings generated.

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

Let's trace through the solution for target = "bac":

Initial state: ans = []

Processing 'b' (target[0]):

  • Current string s = "" (since ans is empty)
  • We need to cycle from 'a' to 'b':
    • Append 'a': t = "" + "a" = "a", add to ans → ans = ["a"]
    • Append 'b': t = "" + "b" = "b", add to ans → ans = ["a", "b"]
    • Since we reached 'b', stop

Processing 'a' (target[1]):

  • Current string s = "b" (last string in ans)
  • We need to cycle from 'a' to 'a':
    • Append 'a': t = "b" + "a" = "ba", add to ans → ans = ["a", "b", "ba"]
    • Since we reached 'a', stop

Processing 'c' (target[2]):

  • Current string s = "ba" (last string in ans)
  • We need to cycle from 'a' to 'c':
    • Append 'a': t = "ba" + "a" = "baa", add to ans → ans = ["a", "b", "ba", "baa"]
    • Append 'b': t = "ba" + "b" = "bab", add to ans → ans = ["a", "b", "ba", "baa", "bab"]
    • Append 'c': t = "ba" + "c" = "bac", add to ans → ans = ["a", "b", "ba", "baa", "bab", "bac"]
    • Since we reached 'c', stop

Final result: ["a", "b", "ba", "baa", "bab", "bac"]

This demonstrates how each character requires starting from 'a' and incrementing through the alphabet until reaching the target character, with all intermediate strings being recorded.

Solution Implementation

1class Solution:
2    def stringSequence(self, target: str) -> List[str]:
3        """
4        Generate a sequence of strings by building up to the target string.
5        For each character in target, append characters from 'a' onwards until
6        reaching the target character.
7      
8        Args:
9            target: The target string to build up to
10          
11        Returns:
12            List of intermediate strings showing the build-up process
13        """
14        from string import ascii_lowercase
15        from typing import List
16      
17        result = []
18      
19        # Process each character in the target string
20        for target_char in target:
21            # Get the current base string (empty if result is empty)
22            current_base = result[-1] if result else ""
23          
24            # Try each letter from 'a' until we reach the target character
25            for letter in ascii_lowercase:
26                # Create new string by appending current letter to base
27                new_string = current_base + letter
28                result.append(new_string)
29              
30                # Stop when we've added the target character
31                if letter == target_char:
32                    break
33      
34        return result
35
1class Solution {
2    /**
3     * Generates a sequence of strings by incrementally building up to the target string.
4     * For each character in the target, it creates intermediate strings by appending
5     * characters from 'a' up to the target character.
6     * 
7     * @param target The target string to build up to
8     * @return A list containing all intermediate strings in the sequence
9     */
10    public List<String> stringSequence(String target) {
11        // Initialize result list to store the sequence of strings
12        List<String> result = new ArrayList<>();
13      
14        // Process each character in the target string
15        for (char targetChar : target.toCharArray()) {
16            // Get the base string (empty if result is empty, otherwise the last string in result)
17            String baseString = result.isEmpty() ? "" : result.get(result.size() - 1);
18          
19            // Generate all intermediate strings by appending characters from 'a' to targetChar
20            for (char currentChar = 'a'; currentChar <= targetChar; ++currentChar) {
21                // Create new string by appending current character to base string
22                String newString = baseString + currentChar;
23                // Add the new string to the result list
24                result.add(newString);
25            }
26        }
27      
28        return result;
29    }
30}
31
1class Solution {
2public:
3    vector<string> stringSequence(string target) {
4        vector<string> result;
5      
6        // Process each character in the target string
7        for (char targetChar : target) {
8            // Get the current base string (empty if result is empty, otherwise the last string in result)
9            string currentBase = result.empty() ? "" : result.back();
10          
11            // Generate all strings from 'a' to targetChar by appending to the base
12            for (char currentChar = 'a'; currentChar <= targetChar; ++currentChar) {
13                // Create new string by appending current character to the base
14                string newString = currentBase + currentChar;
15                // Add the new string to the result sequence
16                result.push_back(newString);
17            }
18        }
19      
20        return result;
21    }
22};
23
1/**
2 * Generates a sequence of strings by progressively building towards a target string.
3 * For each character in the target, it creates intermediate strings by appending
4 * characters from 'a' up to the target character.
5 * 
6 * @param target - The target string to build towards
7 * @returns An array of strings showing the progression
8 */
9function stringSequence(target: string): string[] {
10    // Array to store the sequence of strings
11    const result: string[] = [];
12  
13    // Process each character in the target string
14    for (const targetChar of target) {
15        // Get the previous string as base, or empty string if no previous exists
16        const previousString: string = result.length > 0 ? result[result.length - 1] : '';
17      
18        // Build strings by appending characters from 'a' to the current target character
19        for (let charCode = 'a'.charCodeAt(0); charCode <= targetChar.charCodeAt(0); charCode++) {
20            // Create new string by appending the current character to the base
21            const currentString: string = previousString + String.fromCharCode(charCode);
22            // Add the newly formed string to the result
23            result.push(currentString);
24        }
25    }
26  
27    return result;
28}
29

Time and Space Complexity

Time Complexity: O(n² × |Σ|), where n is the length of the target string and |Σ| is the size of the character set (26 for lowercase letters).

  • The outer loop iterates through each character in the target string: O(n)
  • For each character position, the inner loop iterates through the lowercase letters from 'a' until it reaches the target character
  • In the worst case, this requires iterating through all 26 letters: O(|Σ|)
  • Inside the inner loop, we perform string concatenation s + a, where s can have length up to n-1
  • String concatenation takes O(n) time since strings are immutable in Python
  • Therefore, the total time complexity is O(n) × O(|Σ|) × O(n) = O(n² × |Σ|)

Space Complexity: O(n² × |Σ|)

  • The ans list stores all intermediate strings generated during the process
  • For the first character, we generate at most |Σ| strings of length 1
  • For the second character, we generate at most |Σ| strings of length 2
  • For the i-th character, we generate at most |Σ| strings of length i
  • Total number of strings: at most n × |Σ|
  • Total space for all strings: Σ(i=1 to n) i × |Σ| = |Σ| × n × (n+1) / 2 = O(n² × |Σ|)
  • Additionally, each string operation creates temporary strings, but these don't affect the asymptotic complexity

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

Common Pitfalls

1. Incorrectly handling the string building process

A common mistake is misunderstanding how the keyboard works. Some might think Key 2 modifies the entire last string rather than just the last character:

Incorrect approach:

# Wrong: Trying to modify the entire last string
result = []
for target_char in target:
    if not result:
        result.append("a")
    else:
        # This doesn't follow the key mechanics correctly
        result.append(result[-1] + "a")
  
    # Then trying to change the last character
    while result[-1][-1] != target_char:
        last = result[-1]
        next_char = chr((ord(last[-1]) - ord('a') + 1) % 26 + ord('a'))
        result.append(last[:-1] + next_char)

Correct approach:

# Each intermediate state must be recorded
result = []
for target_char in target:
    base = result[-1] if result else ""
  
    # Must append 'a' first, then cycle through alphabet
    for letter in ascii_lowercase:
        result.append(base + letter)
        if letter == target_char:
            break

2. Not recording all intermediate states

Another pitfall is only recording the final state for each character, missing the intermediate transformations:

Incorrect approach:

# Wrong: Only recording final states
result = []
current = ""
for target_char in target:
    current = current + target_char
    result.append(current)
return result  # Missing all the intermediate 'a', 'b', 'c'... transformations

Correct approach:

# Must record every single key press result
result = []
for target_char in target:
    base = result[-1] if result else ""
    # Record each transformation from 'a' to target_char
    for letter in ascii_lowercase:
        result.append(base + letter)
        if letter == target_char:
            break

3. Handling wrap-around incorrectly

While the problem states Key 2 changes 'z' to 'a', the solution doesn't need explicit wrap-around handling since we always start from 'a' for each new character. However, misunderstanding this could lead to unnecessary complexity:

Overcomplicated approach:

# Unnecessary: Handling wrap-around when not needed
for target_char in target:
    base = result[-1] if result else ""
    current_char = 'a'
    while True:
        result.append(base + current_char)
        if current_char == target_char:
            break
        # Unnecessary wrap-around logic
        current_char = chr((ord(current_char) - ord('a') + 1) % 26 + ord('a'))

Simpler correct approach:

# Just iterate through ascii_lowercase and break when found
for letter in ascii_lowercase:
    result.append(base + letter)
    if letter == target_char:
        break
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which algorithm should you use to find a node that is close to the root of the tree?


Recommended Readings

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

Load More