
Problem Statement
In this task, you are required to identify a type of special string known as a happy string based on given criteria. A happy string is characterized by:
- It is composed solely of the characters from the set
['a', 'b', 'c']
. - No two consecutive characters are identical.
Strings such as "abc", "ac", "b", and "abcbabcbcb" qualify as happy strings because they adhere to the rules outlined above. Contrastingly, strings like "aa", "baa", and "ababbc" do not qualify as they contain repeated consecutive characters.
Given two integers n
and k
, your goal is to generate all possible happy strings of length n
, order them lexicographically, and return the k
-th string in this ordered list. If the number of happy strings of length n
is less than k
, then an empty string should be returned. This challenge requires not only creating these strings but also understanding their ordering and how to efficiently find the desired string.
Examples
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 and Intuition
The task is centered around string generation and manipulation, governed by specific conditions on character arrangement. Here’s a plan to solve the problem:
Understanding Happy Strings:
- They consist of characters 'a', 'b', 'c' only.
- No two adjacent characters in the string are the same.
Generation Strategy:
- Start from an empty string and incrementally build strings of up to length
n
. - For each string, choose the next character such that it does not repeat the last character of the current string.
- Use backtracking to explore all valid combinations of these characters up to the desired length.
- Start from an empty string and incrementally build strings of up to length
Order and Selection:
- While generating strings, store them in a lexicographical manner using Python’s inherent handling of string comparison in arrays.
- After all strings of length
n
have been generated and collected, directly access thek-1
index for thek-th
string (adjusting for 0-indexing). - If
k
exceeds the number of generated strings, this access will be out of bounds, and an empty string should be returned.
Using this method, we can systematically generate the necessary strings and retrieve the desired one based on provided inputs n
and k
.
Examples provided guide through typical inputs and expected outputs, demonstrating simple cases where n = 1
and extends up to where n = 3
, illustrating the scenario where multiple strings are generated and a specific order in a lexicographical list is requested. The final part of fetching the k-th string becomes trivial once we've handled the string generation and ordering correctly.
Solutions
- C++
- Java
- Python
class Solution {
public:
string constructHappyString(int length, int position) {
int totalPossible = 3 * (1 << (length - 1));
if (position > totalPossible) return "";
string happyString(length, 'a');
unordered_map<char, char> smallerNeighbor = {
{'a', 'b'}, {'b', 'a'}, {'c', 'a'}
};
unordered_map<char, char> largerNeighbor = {
{'a', 'c'}, {'b', 'c'}, {'c', 'b'}
};
int startPositionA = 1;
int startPositionB = startPositionA + (1 << (length - 1));
int startPositionC = startPositionB + (1 << (length - 1));
if (position < startPositionB) {
happyString[0] = 'a';
position -= startPositionA;
} else if (position < startPositionC) {
happyString[0] = 'b';
position -= startPositionB;
} else {
happyString[0] = 'c';
position -= startPositionC;
}
for (int i = 1; i < length; i++) {
int midpoint = (1 << (length - i - 1));
if (position < midpoint) {
happyString[i] = smallerNeighbor[happyString[i - 1]];
} else {
happyString[i] = largerNeighbor[happyString[i - 1]];
position -= midpoint;
}
}
return happyString;
}
};
This solution provides a method to construct the k-th lexicographical "happy string" of a given length. A "happy string" is a string where no two adjacent letters are the same, and it only consists of the characters 'a', 'b', and 'c'.
The code initially calculates the total number of possible happy strings using the formula
3 * (1 << (length - 1))
. It returns an empty string if theposition
is greater thantotalPossible
since it is out of the valid range.The
unordered_map
data structuressmallerNeighbor
andlargerNeighbor
facilitate the determination of the next character in the string based on the previous character, ensuring that no two consecutive characters are the same.Depending on the
position
, the first character of the string (happyString[0]
) is determined, then,position
is adjusted relative to the start position of each character block ('a', 'b', 'c').The for loop constructs the string character by character:
- It calculates the
midpoint
for deciding whether the next character should be the smaller or larger neighbor. - Depending on whether the
position
is less than themidpoint
, it assigns the next character inhappyString
.
- It calculates the
Finally, it returns the constructed
happyString
.
This solution effectively utilizes bit manipulation and direct addressing via hash maps to quickly determine each character of the happy string without generating all potential candidates explicitly. This is particularly efficient for generating such strings of considerable length.
public class Solution {
public String generateHappyString(int length, int position) {
int totalStrings = 3 * (1 << (length - 1));
if (position > totalStrings) return "";
StringBuilder happyString = new StringBuilder(length);
for (int i = 0; i < length; i++) {
happyString.append('a');
}
Map<Character, Character> smallerNext = new HashMap<>();
smallerNext.put('a', 'b');
smallerNext.put('b', 'a');
smallerNext.put('c', 'a');
Map<Character, Character> largerNext = new HashMap<>();
largerNext.put('a', 'c');
largerNext.put('b', 'c');
largerNext.put('c', 'b');
int indexA = 1;
int indexB = indexA + (1 << (length - 1));
int indexC = indexB + (1 << (length - 1));
if (position < indexB) {
happyString.setCharAt(0, 'a');
position -= indexA;
} else if (position < indexC) {
happyString.setCharAt(0, 'b');
position -= indexB;
} else {
happyString.setCharAt(0, 'c');
position -= indexC;
}
for (int idx = 1; idx < length; idx++) {
int midPoint = (1 << (length - idx - 1));
if (position < midPoint) {
happyString.setCharAt(
idx,
smallerNext.get(happyString.charAt(idx - 1))
);
} else {
happyString.setCharAt(
idx,
largerNext.get(happyString.charAt(idx - 1))
);
position -= midPoint;
}
}
return happyString.toString();
}
}
This Java program defines a function generateHappyString
that generates the k-th lexicographical "happy string" of a specified length. A happy string is one where no adjacent characters are the same. Here’s a breakdown of how this program functions:
Initial Calculations and Setup:
- First, calculate the total number of possible happy strings of the given length using the formula
3 * (1 << (length - 1))
. - If the requested position exceeds the total number of happy strings, return an empty string.
- Initialize a
StringBuilder
to build the happy string character by character, pre-filled with 'a'.
- First, calculate the total number of possible happy strings of the given length using the formula
Mapping For Next Characters:
- Two mappings (
smallerNext
andlargerNext
) determine the next characters that can follow the current character based on avoiding adjacent repetitions.
- Two mappings (
Determining the Starting Character:
- Calculate indices for each possible starting character ('a', 'b', 'c') of the happy string.
- Use these indices to determine the first character of the happy string based on the given position and adjust the position for subsequent calculations.
Building the String:
- For each subsequent character in the string (from the second to the last), determine whether the position falls within the first half or the second half of the range for the current length. This decision affects whether the next character is chosen from the
smallerNext
orlargerNext
map. - Adjust
position
as needed for the next iteration.
- For each subsequent character in the string (from the second to the last), determine whether the position falls within the first half or the second half of the range for the current length. This decision affects whether the next character is chosen from the
Output:
- Convert the
StringBuilder
to a string and return it, providing the k-th lexicographical happy string of the specified length.
- Convert the
This structured approach efficiently calculates the required happy string by constructing it character by character, optimizing both time and space complexity.
class Solution:
def generateHappyString(self, length: int, position: int) -> str:
num_happy_strings = 3 * (1 << (length - 1)) # Calculate total happy strings
if position > num_happy_strings:
return ""
happy_string = ['a'] * length # Initialize happy_string
# Next available characters based on current character
next_lower = {"a": "b", "b": "a", "c": "a"}
next_upper = {"a": "c", "b": "c", "c": "b"}
# Indices for strings starting with each character
index_a = 1
index_b = index_a + (1 << (length - 1))
index_c = index_b + (1 << (length - 1))
# Set first character
if position < index_b:
happy_string[0] = "a"
position -= index_a
elif position < index_c:
happy_string[0] = "b"
position -= index_b
else:
happy_string[0] = "c"
position -= index_c
# Build the string based on position
for i in range(1, length):
mid_point = 1 << (length - i - 1)
if position < mid_point:
happy_string[i] = next_lower[happy_string[i - 1]]
else:
happy_string[i] = next_upper[happy_string[i - 1]]
position -= mid_point
return "".join(happy_string)
The provided solution in Python generates the k-th lexicographical "happy string" of a given length n
. A "happy string" is a string that meets the condition where no two adjacent characters are the same, and it uses exclusively the characters 'a', 'b', and 'c'.
Here is a breakdown of the approach taken:
Initial Calculations:
- The total number of possible happy strings of length
n
is calculated using the formula3 * (1 << (n - 1))
. - If the requested position
k
(zero-indexed) exceeds the number of happy strings, an empty string is returned as no valid string exists at that position.
- The total number of possible happy strings of length
Initialization:
- An initial happy string is created with a default repeating character 'a', which will be modified as the algorithm progresses.
- Auxiliary dictionaries,
next_lower
andnext_upper
, are defined to facilitate the selection of valid characters that can follow each character without forming an unhappy string.
Position Determination:
- Based on the position index, the initial character of the happy string is determined. This is segmented based on total counts of strings starting with 'a', 'b', or 'c'.
- The position index is adjusted by subtracting the starting index of the respective initial character.
String Construction:
- The string is progressively built character by character from the second position to the end. The construction uses binary decisions based on the midpoint of each subset of possibilities, utilizing the
next_lower
andnext_upper
dictionaries. - This binary choice elegantly divides the possibilities into two groups at each step, refining the placement in the desired k-th position.
- The string is progressively built character by character from the second position to the end. The construction uses binary decisions based on the midpoint of each subset of possibilities, utilizing the
Final Output:
- The result is the concatenation of the characters in the list that formed during the progressive construction based on the provided
position
. This final string is a valid happy string at the requested positionk
.
- The result is the concatenation of the characters in the list that formed during the progressive construction based on the provided
This solution is efficient because it uses bitwise operations to halve the remaining options at each step, thus constructing the string in linear time concerning the length of the string n
.
No comments yet.