Talentd
FleetCode
Make and follow sheets.Sign in

The k-th Lexicographical String of All Happy Strings of Length n solution and explanation

MediumBacktrackingString8 min read87.1% acceptanceUpdated 9 Apr 2026
The k-th Lexicographical String of All Happy Strings of Length n | Leetcode 1415 | codestorywithMIKcodestorywithMIK · 35:25 · 13K viewsWatch on YouTube
9 more videos on this problem
  1. The k-th Lexicographical String of All Happy Strings of Length n | Leetcode 1415 | codestorywithMIKcodestorywithMIK · 35:25 · 13K views
  2. The k-th Lexicographical String of All Happy Strings of Length n - Leetcode 1415 - PythonNeetCodeIO · 18:57 · 10K views
  3. The k th Lexicographical String of All Happy Strings of Length n | Leetcode 1415Techdose · 15:41 · 4K views
  4. 1415. The k-th Lexicographical String of All Happy Strings of Length n | Recrsion | Backtrack | MathAryan Mittal · 30:54 · 2K views
  5. 1415. The k-th Lexicographical String of All Happy Strings of Length n | leetcode daily challenge |ShashCode · 11:51 · 2K views
  6. Daily Leetcode #2174: Mar 14, 2026 - 1415. The k-th Lexicographical String of All Happy Strings ofProgramming Live with Larry · 16:27 · 390 views
  7. 1415. The k-th Lexicographical String of All Happy Strings of Length n | Leetcode Daily - PythonLeetcode Daily · 9:45 · 355 views
  8. LeetCode 1415 | K-th Lexicographical Happy String of Length n | Backtracking Explained 🔥AlgoDiary · 17:12 · 281 views
  9. The k th Lexicographical String of All Happy Strings of Length n || Leetcode || 1415Perceiver · 7:48 · 205 views
  10. LeetCode 1415 | The K-th Lexicographical Happy String 🔥| Backtracking Explained | Java SolutioncodeWithKhan · 23:57 · 130 views

The 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 of i from 1 to s.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.

Example 1:

Input: n = 1, k = 3
Output: "c"
Explanation: The list ["a", "b", "c"] contains all happy strings of length 1. The third string is "c".

Example 2:

Input: n = 1, k = 4
Output: ""
Explanation: There are only 3 happy strings of length 1.

Example 3:

Input: n = 3, k = 9
Output: "cab"
Explanation: There are 12 different happy string of length 3 ["aba", "abc", "aca", "acb", "bab", "bac", "bca", "bcb", "cab", "cac", "cba", "cbc"]. You will find the 9th string = "cab"

Constraints:

  • 1 <= n <= 10
  • 1 <= k <= 100

Approach overview

Problem Overview: Given n and k, generate the k-th lexicographical happy string of length n. A happy string uses characters a, b, and c, and no two adjacent characters can be the same. If fewer than k such strings exist, return an empty string.

Approach 1: Backtracking Generation (Time: O(3 * 2^(n-1)), Space: O(n))

This approach uses backtracking to generate all valid happy strings in lexicographical order. Start with an empty string and recursively append characters from {a,b,c} that differ from the previous character. Each recursive call extends the string until length n. Because the choices are explored in sorted order, the generated strings are already lexicographically ordered. Maintain a counter and stop recursion once the k-th string is found. This method is straightforward and mirrors how you would enumerate combinations during an interview.

Approach 2: Iterative Enumeration with Counting (Time: O(n), Space: O(1) excluding output)

A more efficient method skips full enumeration by using the count of valid strings that start with a specific prefix. The total number of happy strings of length n is 3 * 2^(n-1). After fixing the first character, each additional position has exactly two valid choices (anything except the previous character). Use this property to determine how many strings exist for each prefix. Iterate through possible characters in lexicographical order and check whether the block of strings starting with that prefix contains the k-th string. If not, subtract the block size from k and move to the next candidate. This greedy selection builds the result character by character. The logic relies mainly on counting and simple string construction rather than recursion.

Recommended for interviews: Start with the backtracking solution to show understanding of constraint-based generation using backtracking. Then optimize with the counting-based iterative approach. Interviewers typically expect recognition that the search space follows a predictable pattern (3 * 2^(n-1)), allowing you to directly compute the k-th string in O(n) time.

Approach 1: Backtracking Approach

This approach uses backtracking to generate all happy strings of the specified length n. The basic idea is to build strings character by character, ensuring at each step that the string remains 'happy' by not placing the same character consecutively. Once we have built a string of length n, we check if it is the k-th string that satisfies the conditions. This approach helps efficiently generate and count possible strings.

def getHappyString(n: int, k: int) -> str:
    def backtrack(current: str):
        if len(current) == n:
            nonlocal count
            count += 1
            if count == k:
                nonlocal result
                result = current
            return

        last_char = current[-1] if current else None
        for c in 'abc':
            if c != last_char:
                backtrack(current + c)
                if result:
                    return

    count = 0
    result = ''
    backtrack('')
    return result

print(getHappyString(3, 9))  # Output: "cab"
public class HappyString {

    private int count = 0;
    private String result = "";

    public String getHappyString(int n, int k) {
        backtrack("", n, k);
        return result;
    }

    private void backtrack(String current, int n, int k) {
        if (current.length() == n) {
            count++;
            if (count == k) {
                result = current;
            }
            return;
        }

        char lastChar = current.isEmpty() ? '0' : current.charAt(current.length() - 1);
        for (char c : new char[]{'a', 'b', 'c'}) {
            if (c != lastChar) {
                backtrack(current + c, n, k);
                if (count >= k) return;  // Early termination
            }
        }
    }

    public static void main(String[] args) {
        HappyString hs = new HappyString();
        System.out.println(hs.getHappyString(3, 9));  // Output: "cab"
    }
}
#include <iostream>
#include <string>
using namespace std;

void backtrack(const string &current, int n, int k, int &count, string &result) {
    if (current.length() == n) {
        count++;
        if (count == k) {
            result = current;
        }
        return;
    }

    char lastChar = current.empty() ? '0' : current.back();
    for (char c : "abc") {
        if (c != lastChar) {
            backtrack(current + c, n, k, count, result);
            if (count >= k) return;  // Early exit if we've found k-th string
        }
    }
}

string getHappyString(int n, int k) {
    string result = "";
    int count = 0;
    backtrack("", n, k, count, result);
    return result;
}

int main() {
    cout << getHappyString(3, 9) << endl;  // Output: "cab"
    return 0;
}
function getHappyString(n, k) {
    let count = 0;
    let result = "";

    function backtrack(current) {
        if (current.length === n) {
            count++;
            if (count === k) {
                result = current;
            }
            return;
        }

        const lastChar = current[current.length - 1] || "";
        for (const c of ['a', 'b', 'c']) {
            if (c !== lastChar) {
                backtrack(current + c);
                if (result) return; // Early return when k-th string is found
            }
        }
    }

    backtrack("");
    return result;
}

console.log(getHappyString(3, 9)); // Output: "cab"
#include <stdio.h>
#include <string.h>
#define true 1
#define false 0
typedef int bool;

void generateHappyStrings(char *current, int n, int k, int *count, char *result) {
    if (strlen(current) == n) {
        (*count)++;
        if (*count == k) {
            strcpy(result, current);
        }
        return;
    }
    char lastChar = strlen(current) > 0 ? current[strlen(current) - 1] : '0';
    for (char c = 'a'; c <= 'c'; c++) {
        if (c != lastChar) {
            char nextString[n + 1];
            strcpy(nextString, current);
            nextString[strlen(current)] = c;
            nextString[strlen(current) + 1] = '\0';
            generateHappyStrings(nextString, n, k, count, result);
            if (*count >= k) return;  // Early termination
        }
    }
}

char* getHappyString(int n, int k) {
    char result[n + 1];
    result[0] = '\0';
    int count = 0;
    char initial[1] = "";
    generateHappyStrings(initial, n, k, &count, result);
    if (count < k) return "";  // Less than k strings are possible
    return strdup(result);
}

int main() {
    printf("%s\n", getHappyString(3, 9)); // Output: "cab"
    return 0;
}
using System;

class HappyStringGenerator {
    private int count = 0;
    private string result = string.Empty;

    public string GetHappyString(int n, int k) {
        Backtrack(string.Empty, n, k);
        return result;
    }

    private void Backtrack(string current, int n, int k) {
        if (current.Length == n) {
            count++;
            if (count == k) {
                result = current;
            }
            return;
        }

        char lastChar = current.Length > 0 ? current[current.Length - 1] : '\0';
        foreach (char c in new char[] { 'a', 'b', 'c' }) {
            if (c != lastChar) {
                Backtrack(current + c, n, k);
                if (count >= k) return;  // Early termination once k-th is found
            }
        }
    }

    public static void Main() {
        HappyStringGenerator generator = new HappyStringGenerator();
        Console.WriteLine(generator.GetHappyString(3, 9));  // Output: "cab"
    }
}

In the Python solution, recursion is used within the backtrack function to construct strings while checking they remain happy. We maintain a count of happy strings as they are built and only capture the k-th when it is reached. If less than k happy strings exist, the result remains an empty string.

Complexity

Time complexity is O(3^n), given the recursion depth potential. Space complexity is O(n) because of the recursion calls and temporary string lengths.

Try this approach in the editor

Approach 2: Iterative Enumeration

An iterative approach can be used to generate strings by systematically constructing valid combinations of happy strings. Starting from 'a', 'b', 'c', you extend each string by appending characters that are different from the last one. Use a queue or list data structure for an efficient breadth-first construction of each happy string, and stop when the k-th string is constructed.

from collections import deque

def getHappyString(n, k):
    queue = deque(['a', 'b', 'c'])
    count = 0

    while queue:
        current = queue.popleft()

        if len(current) == n:
            count += 1
            if count == k:
                return current
            continue

        last_char = current[-1]
        for c in 'abc':
            if c != last_char:
                queue.append(current + c)

    return ""  # If k strings are not feasible

print(getHappyString(3, 9))  # Output: "cab"
import java.util.LinkedList;
import java.util.Queue;

public class HappyStringIterative {
    public String getHappyString(int n, int k) {
        Queue<String> queue = new LinkedList<>();
        queue.offer("a");
        queue.offer("b");
        queue.offer("c");
        int count = 0;

        while (!queue.isEmpty()) {
            String current = queue.poll();

            if (current.length() == n) {
                if (++count == k) return current;
                continue;
            }

            char lastChar = current.charAt(current.length() - 1);

            for (char c : new char[]{'a', 'b', 'c'}) {
                if (c != lastChar) queue.offer(current + c);
            }
        }

        return "";  // If k strings not found
    }

    public static void main(String[] args) {
        HappyStringIterative hs = new HappyStringIterative();
        System.out.println(hs.getHappyString(3, 9));  // Output: "cab"
    }
}
#include <iostream>
#include <queue>
#include <string>
using namespace std;

string getHappyString(int n, int k) {
    queue<string> q;
    q.push("a");
    q.push("b");
    q.push("c");
    int count = 0;

    while (!q.empty()) {
        string current = q.front();
        q.pop();

        if (current.length() == n) {
            if (++count == k) return current;
            continue;
        }

        char lastChar = current[current.length() - 1];
        for (char c : { 'a', 'b', 'c' }) {
            if (c != lastChar) q.push(current + c);
        }
    }

    return ""; // if less than k happy strings exist
}

int main() {
    cout << getHappyString(3, 9) << endl;  // Output: "cab"
    return 0;
}
function getHappyString(n, k) {
    const queue = ['a', 'b', 'c'];
    let count = 0;

    while (queue.length > 0) {
        const current = queue.shift();

        if (current.length === n) {
            if (++count === k) return current;
            continue;
        }

        const lastChar = current[current.length - 1];
        for (const c of ['a', 'b', 'c']) {
            if (c !== lastChar) {
                queue.push(current + c);
            }
        }
    }

    return "";  // If we can't form k strings
}

console.log(getHappyString(3, 9));  // Output: "cab"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char *cloneAndAppend(char *s, char c) {
    int len = strlen(s);
    char *newStr = malloc(len + 2);
    strcpy(newStr, s);
    newStr[len] = c;
    newStr[len + 1] = '\0';
    return newStr;
}

char* getHappyString(int n, int k) {
    char *queue[59049]; // Max size when 3^10
    char *initials[] = {"a", "b", "c"};
    int front = 0, rear = 0, count = 0;

    for (int i = 0; i < 3; ++i) {
        queue[rear++] = initials[i];
    }

    while (front < rear) {
        char *current = queue[front++];
        if (strlen(current) == n) {
            if (++count == k) {
                return current;
            }
            free(current);
            continue;
        }

        char lastChar = current[strlen(current) - 1];
        for (char c = 'a'; c <= 'c'; c++) {
            if (c != lastChar) {
                queue[rear++] = cloneAndAppend(current, c);
            }
        }
        free(current);
    }

    return ""; // If k strings are not possible
}

int main() {
    printf("%s\n", getHappyString(3, 9)); // Output: "cab"
    return 0;
}
using System;
using System.Collections.Generic;

class HappyStringIterative {
    public string GetHappyString(int n, int k) {
        Queue<string> queue = new Queue<string>();
        queue.Enqueue("a");
        queue.Enqueue("b");
        queue.Enqueue("c");
        int count = 0;

        while (queue.Count > 0) {
            string current = queue.Dequeue();

            if (current.Length == n) {
                count++;
                if (count == k) return current;
                continue;
            }

            char lastChar = current[current.Length - 1];
            foreach (char c in new char[] { 'a', 'b', 'c' }) {
                if (c != lastChar) queue.Enqueue(current + c);
            }
        }

        return ""; // If k strings aren't possible
    }

    public static void Main() {
        HappyStringIterative hs = new HappyStringIterative();
        Console.WriteLine(hs.GetHappyString(3, 9));  // Output: "cab"
    }
}

In Python's iteration-driven methodology, a deque (double-ended queue) is pivotal for symmetric string extensions from root sequences. Pushing the boundaries of each prefix by permissible extensions, full-length strings are acknowledged by incrementing an auxiliary counter. Functional string retrieval aligns with the k-th offering, punctuated with empty returns when insufficient quantities are calculated.

Complexity

Time complexity is O(3^n), charting full paths through the construction. Space complexity is proportional at O(3^n), corresponding to queue state within the generative cycle.

Try this approach in the editor

Approach 3: DFS

We use a string s to record the current string, initially an empty string. Then, we design a function dfs to generate all happy strings of length n.

The implementation of the function dfs is as follows:

  1. If the length of the current string is equal to n, add the current string to the answer array ans and return;
  2. If the length of the answer array is greater than or equal to k, return directly;
  3. Otherwise, we iterate over the character set {a, b, c}. For each character c, if the current string is empty or the last character of the current string is not equal to c, add the character c to the current string, then recursively call dfs. After the recursion ends, remove the last character of the current string.

Finally, we check if the length of the answer array is less than k. If it is, return an empty string; otherwise, return the k-th element of the answer array.

The time complexity is O(n times 2^n), and the space complexity is O(n). Here, n is the length of the string.

function getHappyString(n: number, k: number): string {
    const ans: string[] = [];
    const s: string[] = [];
    const dfs = () => {
        if (s.length === n) {
            ans.push(s.join(''));
            return;
        }
        if (ans.length >= k) {
            return;
        }
        for (const c of 'abc') {
            if (!s.length || s.at(-1)! !== c) {
                s.push(c);
                dfs();
                s.pop();
            }
        }
    };
    dfs();
    return ans[k - 1] ?? '';
}
func getHappyString(n int, k int) string {
    ans := []string{}
    var s []byte

    var dfs func()
    dfs = func() {
        if len(s) == n {
            ans = append(ans, string(s))
            return
        }
        if len(ans) >= k {
            return
        }
        for c := byte('a'); c <= 'c'; c++ {
            if len(s) == 0 || s[len(s)-1] != c {
                s = append(s, c)
                dfs()
                s = s[:len(s)-1]
            }
        }
    }

    dfs()
    if len(ans) < k {
        return ""
    }
    return ans[k-1]
}
impl Solution {
    pub fn get_happy_string(n: i32, k: i32) -> String {
        let mut ans = Vec::new();
        let mut s = String::new();
        let mut k = k;

        fn dfs(n: i32, s: &mut String, ans: &mut Vec<String>, k: &mut i32) {
            if s.len() == n as usize {
                ans.push(s.clone());
                return;
            }
            if ans.len() >= *k as usize {
                return;
            }
            for c in "abc".chars() {
                if s.is_empty() || s.chars().last() != Some(c) {
                    s.push(c);
                    dfs(n, s, ans, k);
                    s.pop();
                }
            }
        }

        dfs(n, &mut s, &mut ans, &mut k);
        if ans.len() < k as usize {
            "".to_string()
        } else {
            ans[(k - 1) as usize].clone()
        }
    }
}
Try this approach in the editor

Approach 4: Mathematics

We can directly calculate what the k-th happy string is, without generating all happy strings.

Starting from the first happy string of length n, we can determine what each character position should be.

For a happy string of length n, the first character has 3 choices, the second character has 2 choices (cannot be the same as the first), the third character also has 2 choices (cannot be the same as the second), and so on, until the n-th character also has 2 choices (cannot be the same as the (n-1)-th). Therefore, the total number of happy strings of length n is 3 times 2^{n-1}.

If k is greater than the total number of happy strings of length n, we return an empty string directly.

Otherwise, we start from the first character and determine each character's position one by one. For the i-th character, we enumerate the character set {a, b, c}. If the last character of the current string is not equal to c, we calculate the number of remaining happy strings. If k is less than or equal to that count, we append character c to the current string and move on to the next position; otherwise, we subtract that count from k and continue enumerating the next character.

The time complexity is O(n), and the space complexity is O(n), where n is the length of the string.

function getHappyString(n: number, k: number): string {
    if (k > 3 * (1 << (n - 1))) {
        return '';
    }
    const cs = 'abc';
    const ans: string[] = [];
    for (let i = 0; i < n; i++) {
        const remain = 1 << (n - i - 1);
        for (const c of cs) {
            if (ans.at(-1) === c) {
                continue;
            }
            if (k <= remain) {
                ans.push(c);
                break;
            }
            k -= remain;
        }
    }
    return ans.join('');
}
func getHappyString(n int, k int) string {
	if k > 3*(1<<(n-1)) {
		return ""
	}
	cs := "abc"
	ans := make([]byte, 0, n)
	for i := 0; i < n; i++ {
		remain := 1 << (n - i - 1)
		for j := 0; j < len(cs); j++ {
			c := cs[j]
			if len(ans) > 0 && ans[len(ans)-1] == c {
				continue
			}
			if k <= remain {
				ans = append(ans, c)
				break
			}
			k -= remain
		}
	}
	return string(ans)
}
impl Solution {
    pub fn get_happy_string(n: i32, mut k: i32) -> String {
        if k > 3 * (1 << (n - 1)) {
            return String::new();
        }
        let cs = ['a', 'b', 'c'];
        let mut ans: Vec<char> = Vec::with_capacity(n as usize);
        for i in 0..n {
            let remain = 1 << (n - i - 1);
            for &c in &cs {
                if !ans.is_empty() && *ans.last().unwrap() == c {
                    continue;
                }
                if k <= remain {
                    ans.push(c);
                    break;
                }
                k -= remain;
            }
        }
        ans.into_iter().collect()
    }
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Backtracking Approach

Time complexity is O(3^n), given the recursion depth potential. Space complexity is O(n) because of the recursion calls and temporary string lengths.

Iterative Enumeration

Time complexity is O(3^n), charting full paths through the construction. Space complexity is proportional at O(3^n), corresponding to queue state within the generative cycle.

DFS–
Mathematics–

Complexity analysis

ApproachTimeSpaceWhen to Use
Backtracking GenerationO(3 * 2^(n-1))O(n)When demonstrating recursion and constraint-based generation in interviews
Iterative Enumeration with CountingO(n)O(1)When you need the k-th string directly without generating all possibilities

Frequently asked questions

Is The k-th Lexicographical String of All Happy Strings of Length n easy or hard?

The problem is generally rated Medium because the brute-force backtracking approach is straightforward but recognizing the counting pattern requires deeper insight. Understanding that the number of valid strings follows 3 * 2^(n-1) enables a more optimal O(n) solution.

The k-th Lexicographical String of All Happy Strings of Length n Python/Java solution

Python and Java implementations typically follow either recursive backtracking or iterative counting. Backtracking recursively appends characters while skipping duplicates with the previous character. The optimized version computes how many strings exist for each prefix and selects characters greedily to build the result in O(n).

How to solve The k-th Lexicographical String of All Happy Strings of Length n in O(n)?

Use a counting-based greedy method. At each position, check how many valid strings can be formed with a candidate character while respecting the 'no adjacent duplicates' rule. If the block size is smaller than k, subtract it and move to the next candidate; otherwise choose that character and continue. This constructs the result in linear time.

What is the best approach for The k-th Lexicographical String of All Happy Strings of Length n?

The optimal approach uses counting with iterative enumeration. Instead of generating every happy string, calculate how many strings exist for each prefix using the formula 3 * 2^(n-1). This allows you to determine which character belongs at each position and build the k-th lexicographical string directly in O(n) time.

Is The k-th Lexicographical String of All Happy Strings of Length n asked at Google/Amazon/Meta?

Variants of this problem appear in interviews that test backtracking, combinatorial enumeration, and lexicographic ordering. Companies like Google, Amazon, and Meta frequently use similar string generation or k-th permutation style questions to evaluate problem decomposition and counting optimizations.

What data structure is used in The k-th Lexicographical String of All Happy Strings of Length n?

The backtracking solution mainly uses recursion and a dynamic string builder to construct candidates. The optimized solution relies on simple variables and counting logic rather than heavy data structures, making it closer to a greedy combinatorial approach.

What is the time complexity of The k-th Lexicographical String of All Happy Strings of Length n?

The backtracking solution takes O(3 * 2^(n-1)) time because it may generate every valid happy string of length n. The optimized counting approach runs in O(n) time since it determines each character position using precomputed counts rather than exploring all possibilities.

Comments

Now write it yourselfClose this page and solve The k-th Lexicographical String of All Happy Strings of Length n in the editor, without looking.
Practise The k-th Lexicographical String of All Happy Strings of Length n