Under a grammar given below, strings can represent a set of lowercase words. Let's use R(expr)
to denote the set of words the expression represents.
Grammar can best be understood through simple examples:
- Single letters represent a singleton set containing that word.
R("a") = {"a"}
R("w") = {"w"}
- When we take a comma delimited list of 2 or more expressions, we take the union of possibilities.
R("{a,b,c}") = {"a","b","c"}
R("{{a,b},{b,c}}") = {"a","b","c"}
(notice the final set only contains each word at most once)
- When we concatenate two expressions, we take the set of possible concatenations between two words where the first word comes from the first expression and the second word comes from the second expression.
R("{a,b}{c,d}") = {"ac","ad","bc","bd"}
R("a{b,c}{d,e}f{g,h}") = {"abdfg", "abdfh", "abefg", "abefh", "acdfg", "acdfh", "acefg", "acefh"}
Formally, the 3 rules for our grammar:
- For every lowercase letter
x
, we haveR(x) = {x}
- For expressions
e_1, e_2, ... , e_k
withk >= 2
, we haveR({e_1,e_2,...}) = R(e_1) ∪ R(e_2) ∪ ...
- For expressions
e_1
ande_2
, we haveR(e_1 + e_2) = {a + b for (a, b) in R(e_1) × R(e_2)}
, where + denotes concatenation, and × denotes the cartesian product.
Given an expression
representing a set of words under the given grammar, return the sorted list of words that the expression represents.
Example 1:
Input: "{a,b}{c,{d,e}}"
Output: ["ac","ad","ae","bc","bd","be"]
Example 2:
Input: "{{a,z},a{b,c},{ab,z}}"
Output: ["a","ab","ac","z"]
Explanation: Each distinct word is written only once in the final answer.
Constraints:
1 <= expression.length <= 60
expression[i]
consists of'{'
,'}'
,','
or lowercase English letters.- The given
expression
represents a set of words based on the grammar given in the description.
這道題定義了一些花括號的展開規則,比如逗號隔開的就是並列的關系,若字母和括號直接相連,則用字母乘以里面的每一項。若兩個花括號相連,則里面的內容交叉相乘,有點像乘法的分配律的感覺。現在給了一個花括號的表達式,讓我們進行展開,並把最終的結果進行排序。由於這道題里面可能會有多重花括號嵌套,所以展開的順序應該是從內到外,就像是遞歸的思路一樣,先進到最里面,把最內層的處理完了之后,再回溯出來一層一層的處理,直到所有的花括號都完全展開。這里使用一個棧 stack 來模擬這個遞歸的過程,將給定的表達式 expression 先加進棧,還需要一個 HashSet 來避免重復的結果,然后進行 while 循環,條件是棧不為空。在循環中,取出棧頂元素,首先查找左花括號,若不存在的話,說明當前的表達式沒有花括號了,不需要進一步展開了,再檢查一下,假如 visited 中不存在,則加入其中,並且加入到結果 res 中。若包含左花括號,則此時需要進一步操作,需要找到最內層的花括號,方法是用一個 while 循環,只要當前位置不是右花括號則進行循環,在循環中,若遇到左花括號,則進行標記,由於遇到下一個右花括號就直接退出循環了,若遇到新的左花括號則 left 也會被更新,這樣就保證了標記范圍內不會再有左花括號。然后根據標記的左右花括號的位置,將左右兩邊的部分也提取出來,中間雖然沒有其他花括號了,但是還可能有逗號,所以需要拆分所有的逗號分隔的字符串,由於 C++ 中沒有 Java 的 split 函數,只能用字符串流類 istringstream 來老老實實的進行分隔逗號了,將分隔出來的部分左右分別加上 before 和 after,再壓入棧中等待下一次循環即可,進行相同的操作直至所有的花括號均被展開。最后別忘了給結果 res 中的字符串進行排序,參見代碼如下:
class Solution {
public:
vector<string> braceExpansionII(string expression) {
vector<string> res;
unordered_set<string> visited;
stack<string> stk;
stk.push(expression);
while (!stk.empty()) {
string str = stk.top(); stk.pop();
if (str.find("{") == string::npos) {
if (!visited.count(str)) {
visited.insert(str);
res.push_back(str);
}
continue;
}
int i = 0, left = 0, right = 0;
while (str[i] != '}') {
if (str[i++] == '{') left = i - 1;
}
right = i;
string before = str.substr(0, left);
string after = str.substr(right + 1);
string mid = str.substr(left + 1, right - left - 1);
istringstream iss(mid);
string t;
while (getline(iss, t, ',')) {
stk.push(before + t + after);
}
}
sort(res.begin(), res.end());
return res;
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/1096
類似題目:
Brace Expansion
參考資料:
https://leetcode.com/problems/brace-expansion-ii/
https://leetcode.com/problems/brace-expansion-ii/discuss/348541/JAVA-iter_dfs-36ms
https://leetcode.com/problems/brace-expansion-ii/discuss/317890/Simple-solution-(C%2B%2B)