[LeetCode] Pyramid Transition Matrix 金字塔轉變矩陣


 

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:

  1. bottom will be a string with length in range [2, 8].
  2. allowed will have length in range [0, 200].
  3. 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)

https://leetcode.com/problems/pyramid-transition-matrix/discuss/113036/A-map-based-solution-based-on-memoization

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM