Before attempting this problem, you should be comfortable with:
Recursion - Breaking down string parsing problems into subproblems and combining results
String Parsing - Extracting characters and groups from formatted strings with special delimiters
Backtracking - Building combinations incrementally and undoing choices to explore all possibilities
Sorting - Ensuring output is in lexicographical order by sorting options within brace groups
1. Recursion
Intuition
The string consists of segments that are either single characters or groups of options inside braces. We process the string from left to right, extracting the options for each position. For the first position, we get all possible characters (sorted if from braces), then recursively generate all words from the remaining string. Each option at the current position is combined with each word from the recursive result.
Algorithm
Define a helper function to extract options at the current position:
If the character is not '{', return just that character.
Otherwise, collect all characters until '}', sort them, and return the list.
Define a recursive function to generate all words from a starting position:
Base case: if at the end of the string, return a list containing an empty string.
Get the options for the current position and the starting index of the remaining string.
Recursively generate all words from the remaining string.
Combine each option with each word from the recursive result.
Call the recursive function starting at position 0 and return the result.
classSolution:defexpand(self, s:str)-> List[str]:defstoreFirstOptions(startPos, firstOptions):# If the first character is not '{', it means a single characterif s[startPos]!='{':
firstOptions.append(s[startPos])else:# Store all the characters between '{' and '}'while s[startPos]!='}':if'a'<= s[startPos]<='z':
firstOptions.append(s[startPos])
startPos +=1# Sort the list
firstOptions.sort()# Increment it to point to the next character to be consideredreturn startPos +1deffindAllWords(startPos):# Return empty string list if the string is emptyif startPos ==len(s):return[""]
firstOptions =[]# Store the characters for the first index as string in firstOptions
remStringStartPos = storeFirstOptions(startPos, firstOptions)
wordsWithRemString = findAllWords(remStringStartPos)
expandedWords =[]# Create new words by adding the character at the beginningfor c in firstOptions:for word in wordsWithRemString:
expandedWords.append(c + word)return expandedWords
return findAllWords(0)
classSolution{intstoreFirstOptions(String s,int startPos,List<Character> firstOptions){// If the first character is not '{', it means a single characterif(s.charAt(startPos)!='{'){
firstOptions.add(s.charAt(startPos));}else{// Store all the characters between '{' and '}'while(s.charAt(startPos)!='}'){if(s.charAt(startPos)>='a'&& s.charAt(startPos)<='z'){
firstOptions.add(s.charAt(startPos));}
startPos++;}// Sort the listCollections.sort(firstOptions);}// Increment it to point to the next character to be consideredreturn startPos +1;}String[]findAllWords(String s,int startPos){// Return empty string list if the string is emptyif(startPos == s.length()){returnnewString[]{""};}List<Character> firstOptions =newArrayList<>();// Store the characters for the first index as string in firstOptionsint remStringStartPos =storeFirstOptions(s, startPos, firstOptions);String[] wordsWithRemString =findAllWords(s, remStringStartPos);List<String> expandedWords =newArrayList<>();// Create new words by adding the character at the beginningfor(Character c : firstOptions){for(String word : wordsWithRemString){
expandedWords.add(c + word);}}return expandedWords.toArray(newString[0]);}publicString[]expand(String s){returnfindAllWords(s,0);}}
classSolution{public:intstoreFirstOptions(string& s,int startPos, vector<char>& firstOptions){// If the first character is not '{', it means a single characterif(s[startPos]!='{'){
firstOptions.push_back(s[startPos]);}else{// Store all the characters between '{' and '}'while(s[startPos]!='}'){if(s[startPos]>='a'&& s[startPos]<='z'){
firstOptions.push_back(s[startPos]);}
startPos++;}// Sort the listsort(firstOptions.begin(), firstOptions.end());}// Increment it to point to the next character to be consideredreturn startPos +1;}
vector<string>findAllWords(string& s,int startPos){// Return empty string list if the string is emptyif(startPos == s.size()){return{""};}
vector<char> firstOptions;// Store the characters for the first index as string in firstOptionsint remStringStartPos =storeFirstOptions(s, startPos, firstOptions);
vector<string> wordsWithRemString =findAllWords(s, remStringStartPos);
vector<string> expandedWords;// Create new words by adding the character at the beginningfor(char c : firstOptions){for(string word : wordsWithRemString){
expandedWords.push_back(c + word);}}return expandedWords;}
vector<string>expand(string s){returnfindAllWords(s,0);}};
classSolution{/**
* @param {string} s
* @return {string[]}
*/expand(s){conststoreFirstOptions=(startPos, firstOptions)=>{// If the first character is not '{', it means a single characterif(s[startPos]!=='{'){
firstOptions.push(s[startPos]);}else{// Store all the characters between '{' and '}'while(s[startPos]!=='}'){if(s[startPos]>='a'&& s[startPos]<='z'){
firstOptions.push(s[startPos]);}
startPos++;}// Sort the list
firstOptions.sort();}// Increment it to point to the next character to be consideredreturn startPos +1;};constfindAllWords=(startPos)=>{// Return empty string list if the string is emptyif(startPos === s.length){return[""];}const firstOptions =[];// Store the characters for the first index as string in firstOptionsconst remStringStartPos =storeFirstOptions(startPos, firstOptions);const wordsWithRemString =findAllWords(remStringStartPos);const expandedWords =[];// Create new words by adding the character at the beginningfor(const c of firstOptions){for(const word of wordsWithRemString){
expandedWords.push(c + word);}}return expandedWords;};returnfindAllWords(0);}}
publicclassSolution{privateintStoreFirstOptions(string s,int startPos,List<char> firstOptions){// If the first character is not '{', it means a single characterif(s[startPos]!='{'){
firstOptions.Add(s[startPos]);}else{// Store all the characters between '{' and '}'while(s[startPos]!='}'){if(s[startPos]>='a'&& s[startPos]<='z'){
firstOptions.Add(s[startPos]);}
startPos++;}// Sort the list
firstOptions.Sort();}// Increment it to point to the next character to be consideredreturn startPos +1;}privatestring[]FindAllWords(string s,int startPos){// Return empty string list if the string is emptyif(startPos == s.Length){returnnewstring[]{""};}List<char> firstOptions =newList<char>();// Store the characters for the first index as string in firstOptionsint remStringStartPos =StoreFirstOptions(s, startPos, firstOptions);string[] wordsWithRemString =FindAllWords(s, remStringStartPos);List<string> expandedWords =newList<string>();// Create new words by adding the character at the beginningforeach(char c in firstOptions){foreach(string word in wordsWithRemString){
expandedWords.Add(c + word);}}return expandedWords.ToArray();}publicstring[]Expand(string s){returnFindAllWords(s,0);}}
funcexpand(s string)[]string{var storeFirstOptions func(startPos int, firstOptions *[]byte)int
storeFirstOptions =func(startPos int, firstOptions *[]byte)int{// If the first character is not '{', it means a single characterif s[startPos]!='{'{*firstOptions =append(*firstOptions, s[startPos])}else{// Store all the characters between '{' and '}'for s[startPos]!='}'{if s[startPos]>='a'&& s[startPos]<='z'{*firstOptions =append(*firstOptions, s[startPos])}
startPos++}// Sort the list
sort.Slice(*firstOptions,func(i, j int)bool{return(*firstOptions)[i]<(*firstOptions)[j]})}// Increment it to point to the next character to be consideredreturn startPos +1}var findAllWords func(startPos int)[]string
findAllWords =func(startPos int)[]string{// Return empty string list if the string is emptyif startPos ==len(s){return[]string{""}}
firstOptions :=[]byte{}// Store the characters for the first index as string in firstOptions
remStringStartPos :=storeFirstOptions(startPos,&firstOptions)
wordsWithRemString :=findAllWords(remStringStartPos)
expandedWords :=[]string{}// Create new words by adding the character at the beginningfor_, c :=range firstOptions {for_, word :=range wordsWithRemString {
expandedWords =append(expandedWords,string(c)+word)}}return expandedWords
}returnfindAllWords(0)}
class Solution {funexpand(s: String): Array<String>{funstoreFirstOptions(startPos: Int, firstOptions: MutableList<Char>): Int {var pos = startPos
// If the first character is not '{', it means a single characterif(s[pos]!='{'){
firstOptions.add(s[pos])}else{// Store all the characters between '{' and '}'while(s[pos]!='}'){if(s[pos]in'a'..'z'){
firstOptions.add(s[pos])}
pos++}// Sort the list
firstOptions.sort()}// Increment it to point to the next character to be consideredreturn pos +1}funfindAllWords(startPos: Int): List<String>{// Return empty string list if the string is emptyif(startPos == s.length){returnlistOf("")}val firstOptions = mutableListOf<Char>()// Store the characters for the first index as string in firstOptionsval remStringStartPos =storeFirstOptions(startPos, firstOptions)val wordsWithRemString =findAllWords(remStringStartPos)val expandedWords = mutableListOf<String>()// Create new words by adding the character at the beginningfor(c in firstOptions){for(word in wordsWithRemString){
expandedWords.add(c + word)}}return expandedWords
}returnfindAllWords(0).toTypedArray()}}
classSolution{funcexpand(_ s:String)->[String]{let chars =Array(s)funcstoreFirstOptions(_ startPos:Int,_ firstOptions:inout[Character])->Int{var pos = startPos
// If the first character is not '{', it means a single characterif chars[pos]!="{"{
firstOptions.append(chars[pos])}else{// Store all the characters between '{' and '}'while chars[pos]!="}"{if chars[pos]>="a"&& chars[pos]<="z"{
firstOptions.append(chars[pos])}
pos +=1}// Sort the list
firstOptions.sort()}// Increment it to point to the next character to be consideredreturn pos +1}funcfindAllWords(_ startPos:Int)->[String]{// Return empty string list if the string is emptyif startPos == chars.count {return[""]}var firstOptions:[Character]=[]// Store the characters for the first index as string in firstOptionslet remStringStartPos =storeFirstOptions(startPos,&firstOptions)let wordsWithRemString =findAllWords(remStringStartPos)var expandedWords:[String]=[]// Create new words by adding the character at the beginningfor c in firstOptions {for word in wordsWithRemString {
expandedWords.append(String(c)+ word)}}return expandedWords
}returnfindAllWords(0)}}
Time & Space Complexity
Time complexity: O(N⋅3N/7)
Space complexity: O(N⋅3N/7)
Where N is the length of the given string.
2. Iteration
Intuition
Instead of using recursion, we can build the words iteratively. Starting with an empty string, we process each segment of the input. For each segment, we take all current words and extend each one with every option from that segment. This is similar to a Cartesian product, building up the result position by position.
Algorithm
Initialize expandedWords with a single empty string.
Iterate through the input string:
Extract the options at the current position (single char or sorted chars from braces).
For each existing word in expandedWords and each option:
Create a new word by appending the option to the existing word.
Replace expandedWords with the newly created words.
classSolution:defexpand(self, s:str)-> List[str]:defstoreFirstOptions(startPos, firstOptions):# If the first character is not '{', it means a single characterif s[startPos]!='{':
firstOptions.append(s[startPos])else:# Store all the characters between '{' and '}'while s[startPos]!='}':if'a'<= s[startPos]<='z':
firstOptions.append(s[startPos])
startPos +=1# Sort the list
firstOptions.sort()# Increment it to point to the next character to be consideredreturn startPos +1
expandedWords =[""]
startPos =0while startPos <len(s):
firstOptions =[]# Store the characters for the first index as string in firstOptions
remStringStartPos = storeFirstOptions(startPos, firstOptions)
currWords =[]# Append the string in the list firstOptions to string in expandedWordsfor word in expandedWords:for c in firstOptions:
currWords.append(word + c)# Update the list expandedWords to have all the words
expandedWords = currWords
# Pointing to the next character to be considered
startPos = remStringStartPos
return expandedWords
classSolution{intstoreFirstOptions(String s,int startPos,List<String> firstOptions){// If the first character is not '{', it means a single characterif(s.charAt(startPos)!='{'){
firstOptions.add(String.valueOf(s.charAt(startPos)));}else{// Store all the characters between '{' and '}'while(s.charAt(startPos)!='}'){if(s.charAt(startPos)>='a'&& s.charAt(startPos)<='z'){
firstOptions.add(String.valueOf(s.charAt(startPos)));}
startPos++;}// Sort the listCollections.sort(firstOptions);}// Increment it to point to the next character to be consideredreturn startPos +1;}String[]expand(String s){List<String> expandedWords =Arrays.asList("");int startPos =0;while(startPos < s.length()){List<String> firstOptions =newArrayList<>();// Store the characters for the first index as string in firstOptionsint remStringStartPos =storeFirstOptions(s, startPos, firstOptions);List<String> currWords =newArrayList<>();// Append the string in the list firstOptions to string in expandedWordsfor(String word : expandedWords){for(String c : firstOptions){
currWords.add(word + c);}}// Update the list expandedWords to have all the words
expandedWords = currWords;// Pointing to the next character to be considered
startPos = remStringStartPos;}return expandedWords.toArray(newString[0]);}}
classSolution{public:intstoreFirstOptions(string& s,int startPos, vector<string>& firstOptions){// If the first character is not '{', it means a single characterif(s[startPos]!='{'){
firstOptions.push_back(string(1, s[startPos]));}else{// Store all the characters between '{' and '}'while(s[startPos]!='}'){if(s[startPos]>='a'&& s[startPos]<='z'){
firstOptions.push_back(string(1, s[startPos]));}
startPos++;}// Sort the listsort(firstOptions.begin(), firstOptions.end());}// Increment it to point to the next character to be consideredreturn startPos +1;}
vector<string>expand(string s){
vector<string> expandedWords ={""};int startPos =0;while(startPos < s.size()){
vector<string> firstOptions;// Store the characters for the first index as string in firstOptionsint remStringStartPos =storeFirstOptions(s, startPos, firstOptions);
vector<string> currWords;// Append the string in the list firstOptions to string in expandedWordsfor(string word : expandedWords){for(string c : firstOptions){
currWords.push_back(word + c);}}// Update the list expandedWords to have all the words
expandedWords = currWords;// Pointing to the next character to be considered
startPos = remStringStartPos;}return expandedWords;}};
classSolution{/**
* @param {string} s
* @return {string[]}
*/expand(s){conststoreFirstOptions=(startPos, firstOptions)=>{// If the first character is not '{', it means a single characterif(s[startPos]!=='{'){
firstOptions.push(s[startPos]);}else{// Store all the characters between '{' and '}'while(s[startPos]!=='}'){if(s[startPos]>='a'&& s[startPos]<='z'){
firstOptions.push(s[startPos]);}
startPos++;}// Sort the list
firstOptions.sort();}// Increment it to point to the next character to be consideredreturn startPos +1;};let expandedWords =[""];let startPos =0;while(startPos < s.length){const firstOptions =[];// Store the characters for the first index as string in firstOptionsconst remStringStartPos =storeFirstOptions(startPos, firstOptions);const currWords =[];// Append the string in the list firstOptions to string in expandedWordsfor(const word of expandedWords){for(const c of firstOptions){
currWords.push(word + c);}}// Update the list expandedWords to have all the words
expandedWords = currWords;// Pointing to the next character to be considered
startPos = remStringStartPos;}return expandedWords;}}
publicclassSolution{privateintStoreFirstOptions(string s,int startPos,List<string> firstOptions){// If the first character is not '{', it means a single characterif(s[startPos]!='{'){
firstOptions.Add(s[startPos].ToString());}else{// Store all the characters between '{' and '}'while(s[startPos]!='}'){if(s[startPos]>='a'&& s[startPos]<='z'){
firstOptions.Add(s[startPos].ToString());}
startPos++;}// Sort the list
firstOptions.Sort();}// Increment it to point to the next character to be consideredreturn startPos +1;}publicstring[]Expand(string s){List<string> expandedWords =newList<string>{""};int startPos =0;while(startPos < s.Length){List<string> firstOptions =newList<string>();// Store the characters for the first index as string in firstOptionsint remStringStartPos =StoreFirstOptions(s, startPos, firstOptions);List<string> currWords =newList<string>();// Append the string in the list firstOptions to string in expandedWordsforeach(string word in expandedWords){foreach(string c in firstOptions){
currWords.Add(word + c);}}// Update the list expandedWords to have all the words
expandedWords = currWords;// Pointing to the next character to be considered
startPos = remStringStartPos;}return expandedWords.ToArray();}}
funcexpand(s string)[]string{
storeFirstOptions :=func(startPos int, firstOptions *[]string)int{// If the first character is not '{', it means a single characterif s[startPos]!='{'{*firstOptions =append(*firstOptions,string(s[startPos]))}else{// Store all the characters between '{' and '}'for s[startPos]!='}'{if s[startPos]>='a'&& s[startPos]<='z'{*firstOptions =append(*firstOptions,string(s[startPos]))}
startPos++}// Sort the list
sort.Strings(*firstOptions)}// Increment it to point to the next character to be consideredreturn startPos +1}
expandedWords :=[]string{""}
startPos :=0for startPos <len(s){
firstOptions :=[]string{}// Store the characters for the first index as string in firstOptions
remStringStartPos :=storeFirstOptions(startPos,&firstOptions)
currWords :=[]string{}// Append the string in the list firstOptions to string in expandedWordsfor_, word :=range expandedWords {for_, c :=range firstOptions {
currWords =append(currWords, word+c)}}// Update the list expandedWords to have all the words
expandedWords = currWords
// Pointing to the next character to be considered
startPos = remStringStartPos
}return expandedWords
}
class Solution {funexpand(s: String): Array<String>{funstoreFirstOptions(startPos: Int, firstOptions: MutableList<String>): Int {var pos = startPos
// If the first character is not '{', it means a single characterif(s[pos]!='{'){
firstOptions.add(s[pos].toString())}else{// Store all the characters between '{' and '}'while(s[pos]!='}'){if(s[pos]in'a'..'z'){
firstOptions.add(s[pos].toString())}
pos++}// Sort the list
firstOptions.sort()}// Increment it to point to the next character to be consideredreturn pos +1}var expandedWords =mutableListOf("")var startPos =0while(startPos < s.length){val firstOptions = mutableListOf<String>()// Store the characters for the first index as string in firstOptionsval remStringStartPos =storeFirstOptions(startPos, firstOptions)val currWords = mutableListOf<String>()// Append the string in the list firstOptions to string in expandedWordsfor(word in expandedWords){for(c in firstOptions){
currWords.add(word + c)}}// Update the list expandedWords to have all the words
expandedWords = currWords
// Pointing to the next character to be considered
startPos = remStringStartPos
}return expandedWords.toTypedArray()}}
classSolution{funcexpand(_ s:String)->[String]{let chars =Array(s)funcstoreFirstOptions(_ startPos:Int,_ firstOptions:inout[String])->Int{var pos = startPos
// If the first character is not '{', it means a single characterif chars[pos]!="{"{
firstOptions.append(String(chars[pos]))}else{// Store all the characters between '{' and '}'while chars[pos]!="}"{if chars[pos]>="a"&& chars[pos]<="z"{
firstOptions.append(String(chars[pos]))}
pos +=1}// Sort the list
firstOptions.sort()}// Increment it to point to the next character to be consideredreturn pos +1}var expandedWords =[""]var startPos =0while startPos < chars.count {var firstOptions:[String]=[]// Store the characters for the first index as string in firstOptionslet remStringStartPos =storeFirstOptions(startPos,&firstOptions)var currWords:[String]=[]// Append the string in the list firstOptions to string in expandedWordsfor word in expandedWords {for c in firstOptions {
currWords.append(word + c)}}// Update the list expandedWords to have all the words
expandedWords = currWords
// Pointing to the next character to be considered
startPos = remStringStartPos
}return expandedWords
}}
Time & Space Complexity
Time complexity: O(N⋅3N/7)
Space complexity: O(N⋅3N/7)
Where N is the length of the given string.
3. Backtracking
Intuition
Backtracking is a natural fit for generating all combinations. First, we parse the input string to extract all options for each position. Then we build words character by character: at each position, we try each available option, add it to the current word, recurse to the next position, and then remove it (backtrack) to try the next option.
Algorithm
Parse the input string to create a list of options for each position:
For each segment, extract either a single character or sorted characters from braces.
Define a recursive backtracking function:
Base case: if the current string length equals the number of positions, add it to the result.
Get the options for the current position.
For each option:
Append the character to the current string.
Recurse to fill the next position.
Remove the last character (backtrack).
Call the backtracking function with an empty string and return the result.
classSolution:defexpand(self, s:str)-> List[str]:
all_options =[]# Store all options for each positiondefstore_all_options():
pos =0while pos <len(s):
curr_options =[]# If the first character is not '{', it means a single characterif s[pos]!='{':
curr_options.append(s[pos])else:# Store all the characters between '{' and '}'while s[pos]!='}':if'a'<= s[pos]<='z':
curr_options.append(s[pos])
pos +=1# Sort the list
curr_options.sort()
all_options.append(curr_options)
pos +=1defgenerate_words(curr_string, expanded_words):# If the currString is complete, we can store and returniflen(curr_string)==len(all_options):
expanded_words.append(''.join(curr_string))return# Fetch the options for the current index
curr_options = all_options[len(curr_string)]# Add the character and go into recursionfor c in curr_options:
curr_string.append(c)
generate_words(curr_string, expanded_words)# Backtrack to previous state
curr_string.pop()# Store the character options for different indices
store_all_options()
expanded_words =[]
generate_words([], expanded_words)return expanded_words
classSolution{List<List<Character>> allOptions =newArrayList<>();voidstoreAllOptions(String s){for(int pos =0; pos < s.length(); pos++){List<Character> currOptions =newArrayList<>();// If the first character is not '{', it means a single characterif(s.charAt(pos)!='{'){
currOptions.add(s.charAt(pos));}else{// Store all the characters between '{' and '}'while(s.charAt(pos)!='}'){if(s.charAt(pos)>='a'&& s.charAt(pos)<='z'){
currOptions.add(s.charAt(pos));}
pos++;}// Sort the listCollections.sort(currOptions);}
allOptions.add(currOptions);}}voidgenerateWords(StringBuilder currString,List<String> expandedWords){// If the currString is complete, we can store and returnif(currString.length()== allOptions.size()){
expandedWords.add(currString.toString());return;}// Fetch the options for the current indexList<Character> currOptions = allOptions.get(currString.length());// Add the character and go into recursionfor(char c : currOptions){
currString.append(c);generateWords(currString, expandedWords);// Backtrack to previous state
currString.deleteCharAt(currString.length()-1);}}publicString[]expand(String s){// Store the character options for different indicesstoreAllOptions(s);List<String> expandedWords =newArrayList<>();generateWords(newStringBuilder(), expandedWords);return expandedWords.toArray(newString[0]);}}
classSolution{public:
vector<vector<char>> allOptions;voidstoreAllOptions(string& s){for(int pos =0; pos < s.size(); pos++){
vector<char> currOptions;// If the first character is not '{', it means a single characterif(s[pos]!='{'){
currOptions.push_back(s[pos]);}else{// Store all the characters between '{' and '}'while(s[pos]!='}'){if(s[pos]>='a'&& s[pos]<='z'){
currOptions.push_back(s[pos]);}
pos++;}// Sort the listsort(currOptions.begin(), currOptions.end());}
allOptions.push_back(currOptions);}}voidgenerateWords(string currString, vector<string>& expandedWords){// If the currString is complete, we can store and returnif(currString.size()== allOptions.size()){
expandedWords.push_back(currString);return;}// Fetch the options for the current index
vector<char> currOptions = allOptions[currString.size()];// Add the character and go into recursionfor(char c : currOptions){
currString += c;generateWords(currString, expandedWords);// Backtrack to previous state
currString.pop_back();}}
vector<string>expand(string s){// Store the character options for different indicesstoreAllOptions(s);
vector<string> expandedWords;generateWords("", expandedWords);return expandedWords;}};
classSolution{/**
* @param {string} s
* @return {string[]}
*/expand(s){const allOptions =[];// Store all options for each positionconststoreAllOptions=()=>{let pos =0;while(pos < s.length){const currOptions =[];// If the first character is not '{', it means a single characterif(s[pos]!=='{'){
currOptions.push(s[pos]);}else{// Store all the characters between '{' and '}'while(s[pos]!=='}'){if(s[pos]>='a'&& s[pos]<='z'){
currOptions.push(s[pos]);}
pos++;}// Sort the list
currOptions.sort();}
allOptions.push(currOptions);
pos++;}};constgenerateWords=(currString, expandedWords)=>{// If the currString is complete, we can store and returnif(currString.length === allOptions.length){
expandedWords.push(currString.join(''));return;}// Fetch the options for the current indexconst currOptions = allOptions[currString.length];// Add the character and go into recursionfor(const c of currOptions){
currString.push(c);generateWords(currString, expandedWords);// Backtrack to previous state
currString.pop();}};// Store the character options for different indicesstoreAllOptions();const expandedWords =[];generateWords([], expandedWords);return expandedWords;}}
publicclassSolution{privateList<List<char>> allOptions =newList<List<char>>();privatevoidStoreAllOptions(string s){for(int pos =0; pos < s.Length; pos++){List<char> currOptions =newList<char>();// If the first character is not '{', it means a single characterif(s[pos]!='{'){
currOptions.Add(s[pos]);}else{// Store all the characters between '{' and '}'while(s[pos]!='}'){if(s[pos]>='a'&& s[pos]<='z'){
currOptions.Add(s[pos]);}
pos++;}// Sort the list
currOptions.Sort();}
allOptions.Add(currOptions);}}privatevoidGenerateWords(StringBuilder currString,List<string> expandedWords){// If the currString is complete, we can store and returnif(currString.Length == allOptions.Count){
expandedWords.Add(currString.ToString());return;}// Fetch the options for the current indexList<char> currOptions = allOptions[currString.Length];// Add the character and go into recursionforeach(char c in currOptions){
currString.Append(c);GenerateWords(currString, expandedWords);// Backtrack to previous state
currString.Length--;}}publicstring[]Expand(string s){// Store the character options for different indicesStoreAllOptions(s);List<string> expandedWords =newList<string>();GenerateWords(newStringBuilder(), expandedWords);return expandedWords.ToArray();}}
funcexpand(s string)[]string{
allOptions :=[][]byte{}// Store all options for each position
storeAllOptions :=func(){
pos :=0for pos <len(s){
currOptions :=[]byte{}// If the first character is not '{', it means a single characterif s[pos]!='{'{
currOptions =append(currOptions, s[pos])}else{// Store all the characters between '{' and '}'for s[pos]!='}'{if s[pos]>='a'&& s[pos]<='z'{
currOptions =append(currOptions, s[pos])}
pos++}// Sort the list
sort.Slice(currOptions,func(i, j int)bool{return currOptions[i]< currOptions[j]})}
allOptions =append(allOptions, currOptions)
pos++}}var generateWords func(currString []byte, expandedWords *[]string)
generateWords =func(currString []byte, expandedWords *[]string){// If the currString is complete, we can store and returniflen(currString)==len(allOptions){*expandedWords =append(*expandedWords,string(currString))return}// Fetch the options for the current index
currOptions := allOptions[len(currString)]// Add the character and go into recursionfor_, c :=range currOptions {
currString =append(currString, c)generateWords(currString, expandedWords)// Backtrack to previous state
currString = currString[:len(currString)-1]}}// Store the character options for different indicesstoreAllOptions()
expandedWords :=[]string{}generateWords([]byte{},&expandedWords)return expandedWords
}
class Solution {funexpand(s: String): Array<String>{val allOptions = mutableListOf<MutableList<Char>>()// Store all options for each positionfunstoreAllOptions(){var pos =0while(pos < s.length){val currOptions = mutableListOf<Char>()// If the first character is not '{', it means a single characterif(s[pos]!='{'){
currOptions.add(s[pos])}else{// Store all the characters between '{' and '}'while(s[pos]!='}'){if(s[pos]in'a'..'z'){
currOptions.add(s[pos])}
pos++}// Sort the list
currOptions.sort()}
allOptions.add(currOptions)
pos++}}fungenerateWords(currString: StringBuilder, expandedWords: MutableList<String>){// If the currString is complete, we can store and returnif(currString.length == allOptions.size){
expandedWords.add(currString.toString())return}// Fetch the options for the current indexval currOptions = allOptions[currString.length]// Add the character and go into recursionfor(c in currOptions){
currString.append(c)generateWords(currString, expandedWords)// Backtrack to previous state
currString.deleteCharAt(currString.length -1)}}// Store the character options for different indicesstoreAllOptions()val expandedWords = mutableListOf<String>()generateWords(StringBuilder(), expandedWords)return expandedWords.toTypedArray()}}
classSolution{funcexpand(_ s:String)->[String]{let chars =Array(s)var allOptions:[[Character]]=[]// Store all options for each positionfuncstoreAllOptions(){var pos =0while pos < chars.count {var currOptions:[Character]=[]// If the first character is not '{', it means a single characterif chars[pos]!="{"{
currOptions.append(chars[pos])}else{// Store all the characters between '{' and '}'while chars[pos]!="}"{if chars[pos]>="a"&& chars[pos]<="z"{
currOptions.append(chars[pos])}
pos +=1}// Sort the list
currOptions.sort()}
allOptions.append(currOptions)
pos +=1}}funcgenerateWords(_ currString:inout[Character],_ expandedWords:inout[String]){// If the currString is complete, we can store and returnif currString.count == allOptions.count {
expandedWords.append(String(currString))return}// Fetch the options for the current indexlet currOptions = allOptions[currString.count]// Add the character and go into recursionfor c in currOptions {
currString.append(c)generateWords(&currString,&expandedWords)// Backtrack to previous state
currString.removeLast()}}// Store the character options for different indicesstoreAllOptions()var expandedWords:[String]=[]var currString:[Character]=[]generateWords(&currString,&expandedWords)return expandedWords
}}
Time & Space Complexity
Time complexity: O(N⋅3N/7)
Space complexity: O(N)
Where N is the length of the given string.
Common Pitfalls
Forgetting to Sort Options Within Braces
The problem requires the output to be in lexicographically sorted order. Options within braces like {b,a,c} must be sorted to [a,b,c] before generating combinations, otherwise the final result will be out of order.
# Wrong: using options in original order
options =['b','a','c']# Correct: sort first
options.sort()# ['a', 'b', 'c']
Incorrect Index Advancement After Closing Brace
When parsing a brace group, after finding }, you must advance the index past the } character. Off-by-one errors here cause infinite loops or skipped characters.
# Wrong: not advancing past '}'while s[pos]!='}':
pos +=1# Missing: pos += 1 after the loop
Not Handling Single Characters Outside Braces
Single characters outside braces (like a in a{b,c}) should be treated as a group with one option. Forgetting this case causes the character to be skipped entirely in the output.