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.
'{', return just that character.'}', sort them, and return the list.0 and return the result.class Solution:
def expand(self, s: str) -> List[str]:
def storeFirstOptions(startPos, firstOptions):
# If the first character is not '{', it means a single character
if 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 considered
return startPos + 1
def findAllWords(startPos):
# Return empty string list if the string is empty
if 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 beginning
for c in firstOptions:
for word in wordsWithRemString:
expandedWords.append(c + word)
return expandedWords
return findAllWords(0)Time complexity:
Space complexity:
Where is the length of the given string.
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.
expandedWords with a single empty string.expandedWords and each option:expandedWords with the newly created words.expandedWords.class Solution:
def expand(self, s: str) -> List[str]:
def storeFirstOptions(startPos, firstOptions):
# If the first character is not '{', it means a single character
if 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 considered
return startPos + 1
expandedWords = [""]
startPos = 0
while 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 expandedWords
for 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 expandedWordsTime complexity:
Space complexity:
Where is the length of the given string.
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.
class Solution:
def expand(self, s: str) -> List[str]:
all_options = []
# Store all options for each position
def store_all_options():
pos = 0
while pos < len(s):
curr_options = []
# If the first character is not '{', it means a single character
if 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 += 1
def generate_words(curr_string, expanded_words):
# If the currString is complete, we can store and return
if len(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 recursion
for 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_wordsTime complexity:
Space complexity:
Where is the length of the given string.
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']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 loopSingle 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.