3304. Find the K-th Character in String Game I
Problem Description
Alice and Bob are playing a game where Alice starts with a string word = "a"
. Given a positive integer k
, Bob asks Alice to repeatedly perform a specific operation on the string.
The operation works as follows:
- Take the current
word
string - Change each character to its next character in the English alphabet (a→b, b→c, ..., z→a)
- Append this transformed string to the original
word
For example:
- Starting with
"c"
, the operation produces"cd"
(c becomes d, append to get "cd") - Starting with
"zb"
, the operation produces"zbac"
(z→a, b→c, append to get "zbac")
The game continues with Alice performing this operation repeatedly until the string has at least k
characters. Your task is to find and return the k
th character (1-indexed) in the final string.
The solution simulates this process by maintaining an array where each element represents the offset from 'a' for each character. Starting with [0]
(representing "a"), it repeatedly extends the array by adding the incremented values (modulo 26 for wraparound) until the array has at least k
elements. The k
th character is then converted back to its corresponding letter.
Intuition
The key observation is that we need to simulate the string growth process until we have at least k
characters. Since we're only interested in the k
th character, we don't need to store the actual string - we can work with numerical representations.
Think about what happens in each operation:
- We take the current string
- Transform each character to its next letter
- Append the transformed version to the original
This means the string doubles in length with each operation. Starting with "a" (length 1), after one operation we have "ab" (length 2), then "abbc" (length 4), then "abbcbccd" (length 8), and so on.
Instead of working with actual characters, we can represent each character as its offset from 'a'. So 'a' = 0, 'b' = 1, 'c' = 2, and so on. This makes the transformation operation simple arithmetic: just add 1 to each value (using modulo 26 to handle the wraparound from 'z' to 'a').
The pattern becomes clear:
- Start with
[0]
representing "a" - First operation: append
[1]
to get[0, 1]
representing "ab" - Second operation: append
[1, 2]
to get[0, 1, 1, 2]
representing "abbc" - Continue until we have at least
k
elements
Since we only need the k
th character, we can stop as soon as our array reaches length k
, then convert the value at index k-1
back to its corresponding character by adding it to the ASCII value of 'a'.
Solution Approach
The simulation approach uses an array to track the character offsets from 'a' and builds the string iteratively until we have enough characters.
Implementation Steps:
-
Initialize the word array: Start with
word = [0]
, which represents the initial string "a" where 0 is the offset from 'a'. -
Grow the array through operations: While the length of
word
is less thank
, perform the operation:- Take the current
word
array - Create a new array by incrementing each value:
[(x + 1) % 26 for x in word]
- The modulo 26 handles the wraparound (z → a)
- Extend the original array with these new values using
word.extend()
- Take the current
-
Extract the kth character: Once we have at least
k
elements:- Access the element at index
k - 1
(converting from 1-indexed to 0-indexed) - Convert the numerical offset back to a character:
chr(ord("a") + word[k - 1])
- Access the element at index
Example walkthrough with k = 5:
- Initial:
word = [0]
(represents "a") - After operation 1:
word = [0, 1]
(represents "ab") - After operation 2:
word = [0, 1, 1, 2]
(represents "abbc") - After operation 3:
word = [0, 1, 1, 2, 1, 2, 2, 3]
(represents "abbcbccd") - Now
len(word) >= 5
, so we returnchr(ord("a") + word[4])
=chr(97 + 1)
= 'b'
The time complexity is O(k) since we need to generate at least k characters, and the space complexity is also O(k) for storing the array.
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 trace through the solution with k = 10
to find the 10th character.
Initial State:
word = [0]
representing "a"- Length = 1, need to reach at least 10
Operation 1:
- Current:
[0]
- Transform each element:
0 + 1 = 1
- Append
[1]
to get[0, 1]
- This represents "ab"
- Length = 2
Operation 2:
- Current:
[0, 1]
- Transform:
[0+1, 1+1] = [1, 2]
- Append to get
[0, 1, 1, 2]
- This represents "abbc"
- Length = 4
Operation 3:
- Current:
[0, 1, 1, 2]
- Transform:
[1, 2, 2, 3]
- Append to get
[0, 1, 1, 2, 1, 2, 2, 3]
- This represents "abbcbccd"
- Length = 8
Operation 4:
- Current:
[0, 1, 1, 2, 1, 2, 2, 3]
- Transform:
[1, 2, 2, 3, 2, 3, 3, 4]
- Append to get
[0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4]
- This represents "abbcbccdbccdbcde"
- Length = 16
Finding the 10th character:
- We now have 16 elements (≥ 10)
- Access index
k-1 = 9
:word[9] = 2
- Convert to character:
chr(ord('a') + 2) = chr(97 + 2) = 'c'
The 10th character is 'c'.
To verify: the string "abbcbccdbccdbcde" has 'c' at position 10 (1-indexed).
Solution Implementation
1class Solution:
2 def kthCharacter(self, k: int) -> str:
3 """
4 Find the k-th character in a sequence generated by repeatedly appending
5 a modified copy of the current sequence.
6
7 The modification rule: each character is shifted by 1 position in the alphabet
8 (wrapping around from 'z' to 'a').
9
10 Args:
11 k: The 1-indexed position of the character to find
12
13 Returns:
14 The k-th character in the generated sequence
15 """
16 # Initialize the word as a list of integers (0 represents 'a')
17 word = [0]
18
19 # Keep doubling the word until we have at least k characters
20 while len(word) < k:
21 # Create a new portion by incrementing each existing character
22 # Use modulo 26 to wrap around (z -> a)
23 new_portion = [(char_value + 1) % 26 for char_value in word]
24 word.extend(new_portion)
25
26 # Convert the k-th character (1-indexed) from integer to actual character
27 # word[k-1] gets the value at position k (converting from 1-indexed to 0-indexed)
28 # ord('a') gives ASCII value of 'a', add the offset to get the target character
29 kth_char_value = word[k - 1]
30 return chr(ord('a') + kth_char_value)
31
1class Solution {
2 /**
3 * Finds the k-th character in a sequence generated by repeatedly appending
4 * modified copies of the existing sequence.
5 *
6 * Starting with 'a' (represented as 0), each iteration appends a copy of
7 * the current sequence where each character is shifted by 1 in the alphabet
8 * (with wraparound from 'z' to 'a').
9 *
10 * @param k The position of the character to find (1-indexed)
11 * @return The k-th character in the generated sequence
12 */
13 public char kthCharacter(int k) {
14 // Initialize list to store character values (0-25 representing 'a'-'z')
15 List<Integer> characterValues = new ArrayList<>();
16
17 // Start with 'a' (represented as 0)
18 characterValues.add(0);
19
20 // Generate sequence until we have at least k characters
21 while (characterValues.size() < k) {
22 // Store current size to avoid issues with list growth during iteration
23 int currentSize = characterValues.size();
24
25 // Append shifted version of current sequence
26 for (int i = 0; i < currentSize; i++) {
27 // Get current character value, increment by 1, and wrap around at 26
28 int shiftedValue = (characterValues.get(i) + 1) % 26;
29 characterValues.add(shiftedValue);
30 }
31 }
32
33 // Convert the (k-1)th index value to corresponding character and return
34 // k-1 because k is 1-indexed while list is 0-indexed
35 return (char) ('a' + characterValues.get(k - 1));
36 }
37}
38
1class Solution {
2public:
3 char kthCharacter(int k) {
4 // Initialize vector to store character indices (0-based, where 0='a', 1='b', etc.)
5 vector<int> word;
6 word.push_back(0); // Start with 'a' (index 0)
7
8 // Keep generating characters until we have at least k characters
9 while (word.size() < k) {
10 int currentSize = word.size();
11
12 // For each existing character, append its next character in the alphabet
13 for (int i = 0; i < currentSize; ++i) {
14 // Get next character index (with wraparound from 'z' to 'a')
15 int nextCharIndex = (word[i] + 1) % 26;
16 word.push_back(nextCharIndex);
17 }
18 }
19
20 // Convert the k-th character index (1-indexed) to actual character
21 return 'a' + word[k - 1];
22 }
23};
24
1/**
2 * Finds the kth character in a sequence generated by repeatedly appending
3 * the current sequence with each character incremented by 1 (mod 26).
4 * Starting with 'a', the sequence grows as: a -> ab -> abbc -> abbcbccd -> ...
5 *
6 * @param k - The 1-indexed position of the character to find
7 * @returns The kth character in the generated sequence
8 */
9function kthCharacter(k: number): string {
10 // Initialize array with 0 representing 'a'
11 // Each number represents a character offset from 'a' (0-25 for a-z)
12 const characterOffsets: number[] = [0];
13
14 // Keep generating the sequence until we have at least k characters
15 while (characterOffsets.length < k) {
16 // Append the current sequence with each character incremented by 1
17 // Use modulo 26 to wrap 'z' back to 'a'
18 const incrementedSequence: number[] = characterOffsets.map(
19 (offset: number) => (offset + 1) % 26
20 );
21 characterOffsets.push(...incrementedSequence);
22 }
23
24 // Convert the (k-1)th offset to its corresponding character
25 // 97 is the ASCII code for 'a'
26 const characterCode: number = 97 + characterOffsets[k - 1];
27 return String.fromCharCode(characterCode);
28}
29
Time and Space Complexity
Time Complexity: O(k)
The algorithm starts with a list containing one element [0]
. In each iteration of the while loop, it doubles the size of the list by extending it with modified values. The loop continues until len(word) >= k
.
- Initial size: 1
- After iteration 1: 2
- After iteration 2: 4
- After iteration 3: 8
- After iteration
i
:2^i
The loop runs approximately log₂(k)
iterations to reach size k
. However, in each iteration, the extend
operation processes all current elements to create the new elements. The total work done across all iterations is:
- Iteration 1: process 1 element
- Iteration 2: process 2 elements
- Iteration 3: process 4 elements
- ...
Total work = 1 + 2 + 4 + ... + 2^(log₂(k)-1) = 2^(log₂(k)) - 1 ≈ k - 1
Therefore, the time complexity is O(k)
.
Space Complexity: O(k)
The algorithm creates a list word
that grows until its size is at least k
. In the worst case, the list size will be the smallest power of 2 that is greater than or equal to k
, which is at most 2k
. Therefore, the space complexity is O(k)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Inefficient String Concatenation
A common mistake is using string concatenation directly instead of working with numeric representations:
Problematic approach:
def kthCharacter(self, k: int) -> str:
word = "a"
while len(word) < k:
new_part = ""
for char in word:
if char == 'z':
new_part += 'a'
else:
new_part += chr(ord(char) + 1)
word += new_part
return word[k-1]
Why it's inefficient: String concatenation in Python creates new string objects each time, leading to O(k²) time complexity due to repeated string copying.
Solution: Use the numeric array approach as shown in the original solution, or use a list of characters and join at the end.
2. Forgetting the Wraparound from 'z' to 'a'
Without proper modulo operation, the code might fail when encountering 'z':
Incorrect:
new_portion = [char_value + 1 for char_value in word] # Missing % 26
This would produce values beyond 25, causing incorrect characters or runtime errors.
Solution: Always use (char_value + 1) % 26
to ensure values stay within the valid range [0, 25].
3. Off-by-One Error with Indexing
Confusing 0-indexed and 1-indexed positions is a frequent mistake:
Incorrect:
return chr(ord('a') + word[k]) # Should be word[k-1]
Since the problem asks for the k-th character (1-indexed), we need to access word[k-1]
in our 0-indexed array.
4. Unnecessary Full String Generation
Some might generate the entire final string even when only one character is needed:
Inefficient for large k:
def kthCharacter(self, k: int) -> str:
word = "a"
while len(word) < k:
# ... operation ...
# Converting entire array to string unnecessarily
result_string = ''.join([chr(ord('a') + x) for x in word])
return result_string[k-1]
Solution: Only convert the specific character needed: chr(ord('a') + word[k-1])
5. Not Handling Edge Cases
While the problem guarantees k is positive, defensive programming would check:
Better practice:
if k == 1: return 'a' # Quick return for the base case
This optimization avoids unnecessary iterations when k=1.
In a binary min heap, the minimum element can be found in:
Recommended Readings
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
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
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
Want a Structured Path to Master System Design Too? Don’t Miss This!