You are given two integers, n
and k
. A happy string is a string that:
'a'
, 'b'
, and 'c'
.k
-th lexicographically smallest happy string of length n
. If there are less than k
happy strings of length n
, return an empty string.
Key constraints:
At first, you might think about generating all possible strings of length n
using the characters 'a'
, 'b'
, and 'c'
, then filtering out those with consecutive duplicates, and finally sorting them to pick the k
-th one. However, this brute-force approach is inefficient, especially as n
grows, since the number of possible strings increases exponentially.
The key realization is that we can generate happy strings in lexicographical order directly, without generating all possible combinations. By understanding the structure and constraints of happy strings, we can "skip" over large groups of strings that don't meet our criteria or that are not in the correct lexicographical position, allowing us to find the k
-th string efficiently.
This leads to the idea of backtracking or constructive enumeration, where we build the string character by character, always choosing the smallest valid character at each step, and counting how many valid strings are possible from that point onward to guide our search.
We want to efficiently find the k
-th lexicographically smallest happy string of length n
. Here's a step-by-step approach:
k
, we can skip that branch and decrement k
accordingly.n
positions, the total number of happy strings is 3 * 2^(n-1)
(3 choices for the first character, 2 for each subsequent position).n
, check if we've found the k
-th string.This approach efficiently finds the answer without generating all possible happy strings.
Let's use n = 3
and k = 9
as an example.
2^{n-1} = 4
happy strings.k = 9
, it falls into the group starting with 'c'."cab"
.
This step-by-step process shows how we can narrow down the answer without generating every string.
3 * 2^{n-1}
), sort, and select the k-th.n
, and we only traverse one path to the answer.class Solution:
def getHappyString(self, n: int, k: int) -> str:
chars = ['a', 'b', 'c']
res = ""
total = 3 * (2 ** (n-1))
if k > total:
return ""
k -= 1 # zero-based index
prev = ""
for i in range(n):
for c in chars:
if c == prev:
continue
cnt = 2 ** (n - i - 1)
if k >= cnt:
k -= cnt
else:
res += c
prev = c
break
return res
class Solution {
public:
string getHappyString(int n, int k) {
vector<char> chars = {'a', 'b', 'c'};
string res = "";
int total = 3 * (1 << (n-1));
if (k > total) return "";
k--; // zero-based
char prev = 0;
for (int i = 0; i < n; ++i) {
for (char c : chars) {
if (c == prev) continue;
int cnt = 1 << (n - i - 1);
if (k >= cnt) {
k -= cnt;
} else {
res += c;
prev = c;
break;
}
}
}
return res;
}
};
class Solution {
public String getHappyString(int n, int k) {
char[] chars = {'a', 'b', 'c'};
StringBuilder res = new StringBuilder();
int total = 3 * (1 << (n-1));
if (k > total) return "";
k--; // zero-based
char prev = 0;
for (int i = 0; i < n; ++i) {
for (char c : chars) {
if (c == prev) continue;
int cnt = 1 << (n - i - 1);
if (k >= cnt) {
k -= cnt;
} else {
res.append(c);
prev = c;
break;
}
}
}
return res.toString();
}
}
var getHappyString = function(n, k) {
const chars = ['a', 'b', 'c'];
let res = '';
let total = 3 * (1 << (n-1));
if (k > total) return '';
k--; // zero-based
let prev = '';
for (let i = 0; i < n; ++i) {
for (let c of chars) {
if (c === prev) continue;
let cnt = 1 << (n - i - 1);
if (k >= cnt) {
k -= cnt;
} else {
res += c;
prev = c;
break;
}
}
}
return res;
};
The problem of finding the k
-th lexicographical happy string of length n
can be solved efficiently by leveraging combinatorics and lexicographical order. Instead of generating all possible strings, we use recursive construction and counting to "skip" over groups of strings, directly building the answer character by character. This approach is both elegant and efficient, reducing the time complexity from exponential to linear in n
, and demonstrates the power of combining backtracking with combinatorial reasoning.