[LeetCode] 269. Alien Dictionary 另類字典


 

There is a new alien language which uses the latin alphabet. However, the order among letters are unknown to you. You receive a list of non-empty words from the dictionary, where words are sorted lexicographically by the rules of this new language. Derive the order of letters in this language.

Example 1:

Input:
[
  "wrt",
  "wrf",
  "er",
  "ett",
  "rftt"
]

Output: "wertf"

Example 2:

Input:
[
  "z",
  "x"
]

Output: "zx"

Example 3:

Input:
[
  "z",
  "x",
  "z"
] 

Output: "" 

Explanation: The order is invalid, so return "".

Note:

  1. You may assume all letters are in lowercase.
  2. You may assume that if a is a prefix of b, then a must appear before b in the given dictionary.
  3. If the order is invalid, return an empty string.
  4. There may be multiple valid order of letters, return any one of them is fine.

 

這道題讓給了一些按“字母順序”排列的單詞,但是這個字母順序不是我們熟知的順序,而是另類的順序,讓根據這些“有序”的單詞來找出新的字母順序,這實際上是一道有向圖遍歷的問題,跟之前的那兩道 Course Schedule II 和 Course Schedule 的解法很類似,我們先來看 BFS 的解法,需要一個 TreeSet 來保存可以推測出來的順序關系,比如題目中給的例子1,可以推出的順序關系有:

 

t->f
w->e
r->t
e->r

 

這些就是有向圖的邊,對於有向圖中的每個結點,計算其入度,然后從入度為0的結點開始 BFS 遍歷這個有向圖,然后將遍歷路徑保存下來返回即可。下面來看具體的做法:

根據之前講解,需用 TreeSet 來保存這些 pair,還需要一個 HashSet 來保存所有出現過的字母,需要一個一維數組 in 來保存每個字母的入度,另外還要一個 queue 來輔助拓撲遍歷,我們先遍歷單詞集,把所有字母先存入 HashSet,然后我們每兩個相鄰的單詞比較,找出順序 pair,然后根據這些 pair 來賦度,把 HashSet 中入度為0的字母都排入 queue 中,然后開始遍歷,如果字母在 TreeSet 中存在,則將其 pair 中對應的字母的入度減1,若此時入度減為0了,則將對應的字母排入 queue 中並且加入結果 res 中,直到遍歷完成,看結果 res 和 ch 中的元素個數是否相同,若不相同則說明可能有環存在,返回空字符串,參見代碼如下:

 

解法一:

class Solution {
public:
    string alienOrder(vector<string>& words) {
        set<pair<char, char>> st;
        unordered_set<char> ch;
        vector<int> in(256);
        queue<char> q;
        string res;
        for (auto a : words) ch.insert(a.begin(), a.end());
        for (int i = 0; i < (int)words.size() - 1; ++i) {
            int mn = min(words[i].size(), words[i + 1].size()), j = 0;
            for (; j < mn; ++j) {
                if (words[i][j] != words[i + 1][j]) {
                    st.insert(make_pair(words[i][j], words[i + 1][j]));
                    break;
                }
            }
            if (j == mn && words[i].size() > words[i + 1].size()) return "";
        }
        for (auto a : st) ++in[a.second];
        for (auto a : ch) {
            if (in[a] == 0) {
                q.push(a);
                res += a;
            } 
        }
        while (!q.empty()) {
            char c = q.front(); q.pop();
            for (auto a : st) {
                if (a.first == c) {
                    --in[a.second];
                    if (in[a.second] == 0) {
                        q.push(a.second);
                        res += a.second;
                    }
                }
            }
        }
        return res.size() == ch.size() ? res : "";
    }
};

 

下面來看一種 DFS 的解法,思路和 BFS 的很類似,需要建立一個二維的 bool 數組g,為了節省空間,不必像上面的解法中一樣使用一個 HashSet 來記錄所有出現過的字母,可以直接用這個二維數組來保存這個信息,只要 g[i][i] = true,即表示位置為i的字母存在。同時,這個二維數組還可以保存順序對兒的信息,只要 g[i][j] = true,就知道位置為i的字母順序在位置為j的字母前面。找順序對兒的方法跟上面的解法完全相同,之后就可以進行 DFS 遍歷了。由於 DFS 遍歷需要標記遍歷結點,那么就用一個 visited 數組,由於是深度優先的遍歷,並不需要一定要從入度為0的結點開始遍歷,而是從任意一個結點開始都可以,DFS 會遍歷到出度為0的結點為止,加入結果 res,然后回溯加上整條路徑到結果 res 即可,參見代碼如下:

 

解法二:

class Solution {
public:
    string alienOrder(vector<string>& words) {
        vector<vector<bool>> g(26, vector<bool>(26));
        vector<bool> visited(26);
        string res;
        for (string word : words) {
            for (char c : word) {
                g[c - 'a'][c - 'a'] = true;
            }
        }
        for (int i = 0; i < (int)words.size() - 1; ++i) {
            int mn = min(words[i].size(), words[i + 1].size()), j = 0;
            for (; j < mn; ++j) {
                if (words[i][j] != words[i + 1][j]) {
                    g[words[i][j] - 'a'][words[i + 1][j] - 'a'] = true;
                    break;
                }
            }
            if (j == mn && words[i].size() > words[i + 1].size()) return "";
        }
        for (int i = 0; i < 26; ++i) {
            if (!dfs(g, i, visited, res)) return "";
        }
        return res;
    }
    bool dfs(vector<vector<bool>>& g, int idx, vector<bool>& visited, string& res) {
        if (!g[idx][idx]) return true;
        visited[idx] = true;
        for (int i = 0; i < 26; ++i) {
            if (i == idx || !g[i][idx]) continue;
            if (visited[i]) return false;
            if (!dfs(g, i, visited, res)) return false;
        }
        visited[idx] = false;
        g[idx][idx] = false;
        res += 'a' + idx;
        return true;
    }
};

 

Github 同步地址:

https://github.com/grandyang/leetcode/issues/269

 

類似題目:
 

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


免責聲明!

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



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