class Solution:
def braceExpansionII(self, expression: str) -> List[str]:
def parse(expr):
stack = []
cur = set([''])
i = 0
while i < len(expr):
if expr[i] == '{':
count = 1
j = i + 1
while j < len(expr):
if expr[j] == '{':
count += 1
elif expr[j] == '}':
count -= 1
if count == 0:
break
j += 1
sub = parse(expr[i+1:j])
cur = set(a + b for a in cur for b in sub)
i = j
elif expr[i] == ',':
stack.append(cur)
cur = set([''])
else:
j = i
while j < len(expr) and expr[j].isalpha():
j += 1
token = expr[i:j]
cur = set(a + token for a in cur)
i = j - 1
i += 1
stack.append(cur)
res = set()
for s in stack:
res |= s
return res
return sorted(parse(expression))
class Solution {
public:
set<string> parse(const string& expr, int& i) {
set<string> cur{""};
vector<set<string>> stack;
while (i < expr.size() && expr[i] != '}') {
if (expr[i] == '{') {
++i;
set<string> sub = parse(expr, i);
set<string> tmp;
for (const string& a : cur)
for (const string& b : sub)
tmp.insert(a + b);
cur = tmp;
} else if (expr[i] == ',') {
stack.push_back(cur);
cur = {""};
} else if (isalpha(expr[i])) {
int j = i;
while (j < expr.size() && isalpha(expr[j])) ++j;
string token = expr.substr(i, j - i);
set<string> tmp;
for (const string& a : cur)
tmp.insert(a + token);
cur = tmp;
i = j - 1;
}
++i;
}
stack.push_back(cur);
set<string> res;
for (const auto& s : stack)
res.insert(s.begin(), s.end());
return res;
}
vector<string> braceExpansionII(string expression) {
int i = 0;
set<string> res = parse(expression, i);
return vector<string>(res.begin(), res.end());
}
};
class Solution {
public List<String> braceExpansionII(String expression) {
Set<String> res = parse(expression, new int[]{0});
List<String> ans = new ArrayList<>(res);
Collections.sort(ans);
return ans;
}
private Set<String> parse(String expr, int[] i) {
Set<String> cur = new HashSet<>();
cur.add("");
List<Set<String>> stack = new ArrayList<>();
while (i[0] < expr.length() && expr.charAt(i[0]) != '}') {
if (expr.charAt(i[0]) == '{') {
i[0]++;
Set<String> sub = parse(expr, i);
Set<String> tmp = new HashSet<>();
for (String a : cur)
for (String b : sub)
tmp.add(a + b);
cur = tmp;
} else if (expr.charAt(i[0]) == ',') {
stack.add(cur);
cur = new HashSet<>();
cur.add("");
} else if (Character.isLetter(expr.charAt(i[0]))) {
int j = i[0];
while (j < expr.length() && Character.isLetter(expr.charAt(j))) j++;
String token = expr.substring(i[0], j);
Set<String> tmp = new HashSet<>();
for (String a : cur)
tmp.add(a + token);
cur = tmp;
i[0] = j - 1;
}
i[0]++;
}
stack.add(cur);
Set<String> res = new HashSet<>();
for (Set<String> s : stack)
res.addAll(s);
return res;
}
}
var braceExpansionII = function(expression) {
function parse(expr, i) {
let cur = new Set(['']);
let stack = [];
while (i[0] < expr.length && expr[i[0]] !== '}') {
if (expr[i[0]] === '{') {
i[0]++;
let sub = parse(expr, i);
let tmp = new Set();
for (let a of cur)
for (let b of sub)
tmp.add(a + b);
cur = tmp;
} else if (expr[i[0]] === ',') {
stack.push(cur);
cur = new Set(['']);
} else if (/[a-z]/.test(expr[i[0]])) {
let j = i[0];
while (j < expr.length && /[a-z]/.test(expr[j])) j++;
let token = expr.slice(i[0], j);
let tmp = new Set();
for (let a of cur)
tmp.add(a + token);
cur = tmp;
i[0] = j - 1;
}
i[0]++;
}
stack.push(cur);
let res = new Set();
for (let s of stack)
for (let v of s)
res.add(v);
return res;
}
let i = [0];
let res = parse(expression, i);
return Array.from(res).sort();
};
Given a string expression
representing a brace expansion (like in Bash shells), return all possible strings that the expression can generate, sorted in lexicographical order. The expression may contain lowercase letters, commas, and curly braces.
,
) denote a union ("or") operation.a{b,c}
means ab
or ac
).Constraints:
expression.length
≤ 50expression
consists of lowercase letters, '{', '}', and ','At first glance, this problem resembles parsing and evaluating mathematical expressions, but with sets of strings and two main operations: union (via commas) and concatenation (implicit). A brute-force approach might attempt to generate all possible substrings by recursively expanding every brace, but this quickly becomes inefficient with nested braces and repeated sub-expressions.
To optimize, we need to:
Let's break down the solution into clear steps:
This approach ensures that we efficiently build all possible expansions, avoid duplicates, and handle arbitrary levels of nesting.
Let's use the input expression = "{a,b}{c,{d,e}}"
.
{a,b}
to get the set {"a", "b"}
.{c,{d,e}}
:
c
is a string, and {d,e}
expands to {"d", "e"}
.{c,{d,e}}
becomes {"c", "d", "e"}
.["ac", "ad", "ae", "bc", "bd", "be"]
(sorted).This step-by-step process shows how the recursive parser combines union and concatenation to generate all valid strings.
Brute-force approach: If we simply tried every possible way to expand the expression, the time complexity would be exponential in the number of braces and options (i.e., O(2^N) where N is the number of options and nesting).
Optimized recursive approach:
The brace expansion problem is a classic example of recursive parsing and set manipulation. By treating the expression as a grammar with union and concatenation, and by using sets to manage uniqueness, we can efficiently generate all possible expansions, even for deeply nested expressions. The recursive-descent parser breaks the problem into manageable subproblems, and the use of sets and sorting ensures the final output meets the problem's requirements. This approach is robust, elegant, and leverages fundamental programming concepts.