Problem
A happy string is a string that:
- consists only of letters of the set
['a', 'b', 'c']. s[i] != s[i + 1]for all values ofifrom1tos.length - 1(string is 1-indexed).
For example, strings “abc”, “ac”, “b” and “abcbabcbcb” are all happy strings and strings “aa”, “baa” and “ababbc” are not happy strings.
Given two integers n and k, consider a list of all happy strings of length n sorted in lexicographical order.
Return the kth string of this list or return an empty string if there are less than k happy strings of length n.]()
Examples
Example 1:
| |
Example 2:
| |
Example 3:
| |
Constraints:
1 <= n <= 101 <= k <= 100
Solution
Method 1 - Backtracking
The goal is to generate all “happy strings” of length n, sort them lexicographically, and return the kth string. A “happy string” adheres to the following rules:
- It contains only the characters
['a', 'b', 'c']. - Consecutive characters are different.
Given the constraints, generating all strings using recursion is a suitable approach. Using backtracking, we’ll attempt to build strings character by character, ensuring they are “happy” at every step. Once we have all happy strings, we can access the kth string directly or return an empty string if k exceeds the count.
Approach
- Backtracking: Start with an empty string and try appending each character
['a', 'b', 'c'], ensuring no two consecutive characters are the same. - Base Case: Stop when the string reaches the desired length
n, and add it to a result array. - Lexicographical Order: Backtracking naturally produces strings in lexicographical order due to the order of character choices.
- Check if the number of generated happy strings is at least
k. If yes, return thekth string; otherwise, return an empty string.
Video explanation
Here is the video explaining this method in detail. Please check it out:
Code
| |
| |
| |
| |
Complexity
- ⏰ Time complexity:
O(2^n)- Generating all happy strings of length
ninvolvesO(2^n)operations as, for each position, we have at most 2 valid choices (excluding the previous character). - Accessing the
kth string from the stored list isO(1), so the overall time complexity isO(2^n).
- Generating all happy strings of length
- 🧺 Space complexity:
O(2^n)- Storing all happy strings requires
O(2^n)space. - The recursive call stack takes
O(n)space. - Hence, the overall space complexity is
O(2^n).
- Storing all happy strings requires
Dry Runs
n = 3, k = 4
For n = 3, k = 4:
- Generate All Happy Strings of Length 3:
- Backtracking generates:
["aba", "abc", "aca", "acb", "bab", "bac", "bca", "bcb", "cab", "cac", "cba", "cbc"](lexicographical order).
- Backtracking generates:
- Access the 4th string:
"acb".
graph TD
S["Start (Empty String)"] --> |a| A["a"]
S --> |b| B["b"]
S --> |c| C["c"]
A --> |b| AA["ab"]
A --> |c| AB["ac"]
B --> |a| BA["ba"]
B --> |c| BB["bc"]
C --> |a| CA["ca"]
C --> |b| CB["cb"]
AA --> |a| AAA["aba"]
AA --> |c| AAB["abc"]
AB --> |a| ABA["aca"]
AB --> |b| ABB["acb"]
BA --> |b| BAA["bab"]
BA --> |c| BAB["bac"]
BB --> |a| BBA["bca"]
BB --> |b| BBB["bcb"]
CA --> |b| CAA["cab"]
CA --> |c| CAB["cac"]
CB --> |a| CBA["cba"]
CB --> |c| CBB["cbc"]
n = 2, k = 8
For n = 2, k = 8 (when k exceeds count):
- Generated Happy Strings:
["ab", "ac", "ba", "bc", "ca", "cb"]. - Since
k = 8exceeds the count, return an empty string.
Method 2 - Using Maths
If you observe, the recursion tree is very symmetric.
Instead of generating all the happy strings explicitly (which results in exponential time complexity), we can use the ordered and structured nature of the happy strings to directly compute the kth happy string without generating all the others.
Symmetry observation
- For a given
n, the strings branch at every position (a,b,c), resulting in a tree structure. - Each valid branch creates half (
2/3) of the total possibilities at each step due to the restriction (no consecutive characters are the same). - For example, at every level:
- If the previous character is
'a', the valid choices are'b'and'c'. - If the previous character is
'b', the valid choices are'a'and'c'. - This reduces the problem to selecting a character based on the
kvalue and recursively narrowing down.
- If the previous character is
Mathematical Optimisation
- The total number of happy strings of length
nis3 * 2^(n-1)(derived from 3 choices for the first character and 2 choices thereafter). - If we know the
kvalue, we can choose the next character at each level based on which regionkfalls into without generating all possibilities.
Algorithm
- Start with an empty result string.
- Compute the total number of happy strings of length
n(let it betotal). - For each position in the string (from
0ton-1):- Divide the space of possibilities equally among valid branches (depending on the previous character).
- Narrow down to the correct branch based on
k. - Append the corresponding character to the result.
- Update
kto represent the relative position within the current branch.
Code
| |
| |
Complexity
- ⏰ Time complexity:
O(n)as at each step, we compute the valid choices and reduce the problem size in constant time. - 🧺 Space complexity:
O(1)
Dry Run
Here are the steps:
Step 1:
- Total happy strings:
3 * 2^(3-1) = 12. - First character has 3 groups (
a,b,c), each with size2^(3-1) = 4. k = 4falls in the first group (a).
Step 2 (fix the first character to a):
- Within the
agroup (ab,ac), each second-level group has size2^(3-2) = 2. k = 4is on the second group (ac).
Step 3 (fix the first two characters to ac):
- Within the
acgroup (aca,acb), each group has size2^(3-3) = 1. k = 4picks the second string (acb).
Result: "acb"