Problem of The Day: The k-th Lexicographical String of All Happy Strings of Length n
Problem Statement
Intuition
The problem requires generating a lexicographically sorted list of “happy strings” of length n
using the characters ‘a’, ‘b’, and ‘c’. A “happy string” is defined as a string where no two consecutive characters are the same. Given an integer k
, we return the k
-th lexicographically smallest happy string.
The first intuition is to use backtracking to generate all possible happy strings and store them in a min-heap to track the k
-th smallest string efficiently.
Approach
- Use a recursive function
generate_happy_string
to generate all possible happy strings of lengthn
. - Maintain a min-heap (
min_heap
) to store the happy strings in lexicographic order. - At each step, append one of the characters (‘a’, ‘b’, or ‘c’) to the current string, ensuring that the last character is not repeated.
- Once the length of the string reaches
n
, add it to the heap. - Return the
k
-th smallest string from the heap, or an empty string ifk
is out of bounds.
Complexity
-
Time Complexity:
- Since each position in the string can be filled with at most 2 choices (excluding the previous character), the total number of happy strings is at most
O(2^n)
. Since heap operations takeO(log k)
, the overall complexity is approximatelyO(2^n log k)
.
- Since each position in the string can be filled with at most 2 choices (excluding the previous character), the total number of happy strings is at most
-
Space Complexity:
- The recursion depth is
O(n)
, and the heap can store up toO(2^n)
elements in the worst case. Thus, the space complexity isO(2^n)
.
- The recursion depth is
Code
import heapq
class Solution:
def getHappyString(self, n: int, k: int) -> str:
min_heap = []
def generate_happy_string(letters, curr, start):
if len(curr) == n:
heapq.heappush(min_heap, "".join(curr))
return
for i in range(3):
if curr and curr[-1] == letters[i]:
continue
generate_happy_string(letters, curr + [letters[i]], i)
generate_happy_string("abc", [], 0)
return min_heap[k - 1] if 0 <= k - 1 < len(min_heap) else ""
Editorial
Approach 1: Backtracking
class Solution:
def getHappyString(self, n: int, k: int) -> str:
current_string = ""
happy_strings = []
# Generate all happy strings of length n
self.generate_happy_strings(n, current_string, happy_strings)
# Check if there are at least k happy strings
if len(happy_strings) < k:
return ""
# Sort the happy strings in lexicographical order
happy_strings.sort()
return happy_strings[k - 1]
def generate_happy_strings(
self, n: int, current_string: str, happy_strings: list
):
# If the current string has reached the desired length, add it to the list
if len(current_string) == n:
happy_strings.append(current_string)
return
# Try adding each character ('a', 'b', 'c') to the current string
for current_char in ["a", "b", "c"]:
# Skip if the current character is the same as the last character
if len(current_string) > 0 and current_string[-1] == current_char:
continue
# Recursively generate the next character
self.generate_happy_strings(
n, current_string + current_char, happy_strings
)
- time: O(n * 2^n)
- space: O(2^n)
Approach 2: Optimized Recursion
class Solution:
def getHappyString(self, n: int, k: int) -> str:
self.current_string = ""
self.result = ""
self.index_in_sorted_list = 0
# Generate happy strings and track the k-th string
self.generate_happy_strings(n, k)
return self.result
def generate_happy_strings(self, n, k):
# If the current string has reached the desired length
if len(self.current_string) == n:
# Increment the count of generated strings
self.index_in_sorted_list += 1
# If this is the k-th string, store it in the result
if self.index_in_sorted_list == k:
self.result = self.current_string
return
# Try adding each character ('a', 'b', 'c') to the current string
for current_char in ["a", "b", "c"]:
# Skip if the current character is the same as the last one
if (
len(self.current_string) > 0
and self.current_string[-1] == current_char
):
continue
self.current_string += current_char
# Recursively generate the next character
self.generate_happy_strings(n, k)
# If the result is found, stop further processing
if self.result != "":
return
# Backtrack by removing the last character
self.current_string = self.current_string[:-1]
Approach 3: Iterative Using a Stack
class Solution:
def getHappyString(self, n: int, k: int) -> str:
strings_stack = []
index_in_sorted_list = 0
strings_stack.append("") # Start with an empty string
while strings_stack:
current_string = strings_stack.pop()
# If we have built a string of length n, count it
if len(current_string) == n:
index_in_sorted_list += 1
# If we reach the k-th happy string, return it
if index_in_sorted_list == k:
return current_string
continue
# Expand the current string by adding 'a', 'b', or 'c'
# Ensuring lexicographic order by pushing in reverse
for current_char in reversed(["a", "b", "c"]):
# Avoid consecutive identical characters
if (
len(current_string) == 0
or current_string[-1] != current_char
):
strings_stack.append(current_string + current_char)
return ""