We are stacking blocks to form a pyramid. Each block has a color which is a one letter string, like `'Z'`.
For every block of color `C` we place not in the bottom row, we are placing it on top of a left block of color `A` and right block of color `B`. We are allowed to place the block there only if `(A, B, C)` is an allowed triple.
We start with a bottom row of bottom
, represented as a single string. We also start with a list of allowed triples allowed
. Each allowed triple is represented as a string of length 3.
Return true if we can build the pyramid all the way to the top, otherwise false.
Example 1:
Input: bottom = "XYZ", allowed = ["XYD", "YZE", "DEA", "FFF"] Output: true Explanation: We can stack the pyramid like this: A / \ D E / \ / \ X Y Z This works because ('X', 'Y', 'D'), ('Y', 'Z', 'E'), and ('D', 'E', 'A') are allowed triples.
Example 2:
Input: bottom = "XXYX", allowed = ["XXX", "XXY", "XYX", "XYY", "YXZ"] Output: false Explanation: We can't stack the pyramid to the top. Note that there could be allowed triples (A, B, C) and (A, B, D) with C != D.
Note:
bottom
will be a string with length in range[2, 8]
.allowed
will have length in range[0, 200]
.- Letters in all strings will be chosen from the set
{'A', 'B', 'C', 'D', 'E', 'F', 'G'}
.
這道題讓我們累一個金字塔,用字母來代表每塊磚的顏色,給了一個allowed數組,里面都是長度為三的字符串,比如“ABC”表示C可以放在A和B的上方,注意AB上面也可以放其他的,比如“ABD”也可以同時出現,不過搭積木的時候只能選擇一種。給了我們一個bottom字符串,是金字塔的底層,問我們能不能搭出一個完整的金字塔。那么實際上我們就是從底層開始,一層一層的向上來累加,直到搭出整個金字塔。我們先來看遞歸的解法,首先由於我們想快速知道兩個字母上方可以放的字母,需要建立基座字符串和上方字符集合之間的映射,由於上方字符可以不唯一,所以用個HashSet來放字符。我們的遞歸函數有三個參數,當前層字符串cur,上層字符串above,還有我們的HashMap。如果cur的大小為2,above的大小為1,那么說明當前已經達到金字塔的頂端了,已經搭出來了,直接返回true。否則看,如果上一層的長度比當前層的長度正好小一個,說明上一層也搭好了,我們現在去搭上上層,於是調用遞歸函數,將above當作當前層,空字符串為上一層,將調用的遞歸函數結果直接返回。否則表示我們還需要繼續去搭above層,我們先算出above層的長度pos,然后從當前層的pos位置開始取兩個字符,就是above層接下來需要搭的字符的基座字符,舉個例子如下:
D / \ / \ A B C
我們看到現在above層只有一個D,那么pos為1,在cur層1位置開始取兩個字符,得到"BC",即是D的下一個位置的字符的基座字符串base。取出了base后,如果HashMap中有映射,則我們遍歷其映射的字符集合中的所有字符,對每個字符都調用遞歸函數,此時above字符串需要加上這個遍歷到的字符,因為我們在嘗試填充這個位置,如果有返回true的,那么當前遞歸函數就返回true了,否則最終返回false,參見代碼如下:
解法一:
class Solution { public: bool pyramidTransition(string bottom, vector<string>& allowed) { unordered_map<string, unordered_set<char>> m; for (string str : allowed) { m[str.substr(0, 2)].insert(str[2]); } return helper(bottom, "", m); } bool helper(string cur, string above, unordered_map<string, unordered_set<char>>& m) { if (cur.size() == 2 && above.size() == 1) return true; if (above.size() == cur.size() - 1) return helper(above, "", m); int pos = above.size(); string base = cur.substr(pos, 2); if (m.count(base)) { for (char ch : m[base]) { if (helper(cur, above + string(1, ch), m)) { return true; } } } return false; } };
下面來看一種迭代的寫法,這是一種DP的解法,建立一個三維的dp數組,其中dp[i][j][ch]表示在金字塔(i, j)位置上是否可以放字符ch,金字塔的寬和高已經確定了,都是n。每個位置對應着nxn的數組的左半邊,如下所示:
F _ _
D E _
A B C
除了底層,每個位置可能可以放多個字符,所以這里dp數組是一個三維數組,第三維的長度為7,因為題目中限定了字母只有A到G共7個,如果dp值為true,表示該位置放該字母,我們根據bottom字符串來初始化dp數組的底層。這里還是需要一個HashMap,不過跟上面的解法略有不同的是,我們建立上方字母跟其能放的基座字符串集合的映射,因為一個字母可能可以放多個位置,所以用個集合來表示。然后我們就開始從倒數第二層開始往頂部更新啦,對於金字塔的每個位置,我們都遍歷A到G中所有的字母,如果當前字母在HashMap中有映射,則我們遍歷對應的基座字符串集合中的所有字符串,基座字符串共有兩個字母,左邊的字母對應的金字塔中的位置是(i + 1, j),右邊的字母對應的位置是(i + 1, j + 1),我們只要在這兩個位置上分別查詢對應的字母的dp值是否為true,是的話,說明當前位置有字母可以放,我們將當前位置的字母對應的dp值賦值為true。這樣,當我們整個更新完成了之后,我們只要看金字塔頂端位置(0, 0)是否有字母可以放,有的話,說明可以搭出金字塔,返回true,否則返回false,參見代碼如下:
解法二:
class Solution { public: bool pyramidTransition(string bottom, vector<string>& allowed) { int n = bottom.size(); vector<vector<vector<bool>>> dp(n, vector<vector<bool>>(n, vector<bool>(7, false))); unordered_map<char, unordered_set<string>> m; for (string str : allowed) { m[str[2]].insert(str.substr(0, 2)); } for (int i = 0; i < n; ++i) { dp[n - 1][i][bottom[i] - 'A'] = true; } for (int i = n - 2; i >= 0; --i) { for (int j = 0; j <= i; ++j) { for (char ch = 'A'; ch <= 'G'; ++ch) { if (!m.count(ch)) continue; for (string str : m[ch]) { if (dp[i + 1][j][str[0] - 'A'] && dp[i + 1][j + 1][str[1] - 'A']) { dp[i][j][ch - 'A'] = true; } } } } } for (int i = 0; i < 7; ++i) { if (dp[0][0][i]) return true; } return false; } };
參考資料:
https://leetcode.com/problems/pyramid-transition-matrix/discuss/113075/DP-O(n2-*-m)