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 words from the dictionary, where words are sorted lexicographically by the rules of this new language. Derive the order of letters in this language.
For example,
Given the following words in dictionary,
[ "wrt", "wrf", "er", "ett", "rftt" ]
The correct order is: "wertf"
.
Note:
- You may assume all letters are in lowercase.
- If the order is invalid, return an empty string.
- There may be multiple valid order of letters, return any one of them is fine.
給一個單詞字典,單詞是按照字典序排序,求字母的排序。以題中例子,先看所有單詞的第1個字符,可知順序是w->e-r。然后對於兩個連續的單詞,找到第一個不相同的字符,比如 wrt和wrf,wr之后t在f之前,所以排序是 t->f。按照當前字母前面出現的字母個數排序,比如w前面有0個字母,e前面有w一個字母,r前面有e和w兩個字母,所以排序是w->e->r。因此可以歸結為一個拓撲問題,先建圖,然后進行遍歷。首先統計入度:w的入度是0,e的入度是1,r的入度是2。先把入度為0的節點放入結果中,然后取出w后面連接的節點,將他們的入度-1,如果有入度為0的節點,再放入結果中。
time: 建圖->O(n*k), Topological sort-> O(26 + n) = O(n),space: O(n),主要是Map的大小,k表示單詞平均長度。
在圖論中,拓撲排序(Topological Sorting)是一個有向無環圖(DAG, Directed Acyclic Graph)的所有頂點的線性序列。拓撲排序通常用來“排序”具有依賴關系的任務。
該序列必須滿足下面兩個條件:1)每個頂點出現且只出現一次。2)若存在一條從頂點 A 到頂點 B 的路徑,那么在序列中頂點 A 出現在頂點 B 的前面。
如何寫出它的拓撲排序呢?這里說一種比較常用的方法:
- 從 DAG 圖中選擇一個沒有前驅(即入度為0)的頂點並輸出。
- 從圖中刪除該頂點和所有以它為起點的有向邊。
- 重復 1 和 2 直到當前的 DAG 圖為空或當前圖中不存在無前驅的頂點為止。后一種情況說明有向圖中必然存在環。
Java:
public class Solution { public String alienOrder(String[] words) { // Topological sorting - Kahn's Algorithm if(words == null || words.length == 0) { return ""; } Map<Character, Set<Character>> graph = new HashMap<>(); Set<Character> set = new HashSet<>(); for (String word : words) { for (int i = 0; i < word.length(); i++) { set.add(word.charAt(i)); } } int[] inDegree = new int[26]; for (int k = 1; k < words.length; k++) { String preStr = words[k - 1]; String curStr = words[k]; for (int i = 0; i < Math.min(preStr.length(), curStr.length()); i++) { char preChar = preStr.charAt(i); char curChar = curStr.charAt(i); if (preChar != curChar) { if (!graph.containsKey(preChar)) { graph.put(preChar, new HashSet<Character>()); } if (!graph.get(preChar).contains(curChar)) { inDegree[curChar - 'a']++; } graph.get(preChar).add(curChar); break; } } } Queue<Character> queue = new LinkedList<>(); for (int i = 0; i < inDegree.length; i++) { if (inDegree[i] == 0) { char c = (char)('a' + i); if (set.contains(c)) { queue.offer(c); } } } StringBuilder sb = new StringBuilder(); while (!queue.isEmpty()) { char c = queue.poll(); sb.append(c); if (graph.containsKey(c)) { for (char l : graph.get(c)) { inDegree[l - 'a']--; if (inDegree[l - 'a'] == 0) { queue.offer(l); } } } } return sb.length() != set.size() ? "" : sb.toString(); } }
Python:BFS
class Solution(object): def alienOrder(self, words): """ :type words: List[str] :rtype: str """ result, zero_in_degree_queue, in_degree, out_degree = [], collections.deque(), {}, {} nodes = sets.Set() for word in words: for c in word: nodes.add(c) for i in xrange(1, len(words)): if len(words[i-1]) > len(words[i]) and \ words[i-1][:len(words[i])] == words[i]: return "" self.findEdges(words[i - 1], words[i], in_degree, out_degree) for node in nodes: if node not in in_degree: zero_in_degree_queue.append(node) while zero_in_degree_queue: precedence = zero_in_degree_queue.popleft() result.append(precedence) if precedence in out_degree: for c in out_degree[precedence]: in_degree[c].discard(precedence) if not in_degree[c]: zero_in_degree_queue.append(c) del out_degree[precedence] if out_degree: return "" return "".join(result) # Construct the graph. def findEdges(self, word1, word2, in_degree, out_degree): str_len = min(len(word1), len(word2)) for i in xrange(str_len): if word1[i] != word2[i]: if word2[i] not in in_degree: in_degree[word2[i]] = sets.Set() if word1[i] not in out_degree: out_degree[word1[i]] = sets.Set() in_degree[word2[i]].add(word1[i]) out_degree[word1[i]].add(word2[i]) break
Python:DFS
class Solution2(object): def alienOrder(self, words): # Find ancestors of each node by DFS. nodes, ancestors = sets.Set(), {} for i in xrange(len(words)): for c in words[i]: nodes.add(c) for node in nodes: ancestors[node] = [] for i in xrange(1, len(words)): if len(words[i-1]) > len(words[i]) and \ words[i-1][:len(words[i])] == words[i]: return "" self.findEdges(words[i - 1], words[i], ancestors) # Output topological order by DFS. result = [] visited = {} for node in nodes: if self.topSortDFS(node, node, ancestors, visited, result): return "" return "".join(result) # Construct the graph. def findEdges(self, word1, word2, ancestors): min_len = min(len(word1), len(word2)) for i in xrange(min_len): if word1[i] != word2[i]: ancestors[word2[i]].append(word1[i]) break # Topological sort, return whether there is a cycle. def topSortDFS(self, root, node, ancestors, visited, result): if node not in visited: visited[node] = root for ancestor in ancestors[node]: if self.topSortDFS(root, ancestor, ancestors, visited, result): return True result.append(node) elif visited[node] == root: # Visited from the same root in the DFS path. # So it is cyclic. return True return False
C++:
class Solution { public: string alienOrder(vector<string>& words) { if(words.size() == 0) return ""; unordered_map<char, vector<char>> d; unordered_map<char, bool> used; for(auto s : words) { for(int i = 0; i < s.length(); i++) { if(used.find(s[i]) == used.end()) used.insert(pair<char, bool>(s[i], false)); } } for(int i = 1; i < words.size(); i++) { string cur = words[i]; string pre = words[i - 1]; int j = 0; while(j < min(cur.length(), pre.length())) { if(cur[j] != pre[j]) { if(d.find(pre[j]) == d.end()) { vector<char> list; list.push_back(cur[j]); d.insert(pair<char, vector<char>>(pre[j], list)); } else { d[pre[j]].push_back(cur[j]); } break; } j++; } } string result = ""; for(auto it = d.begin(); it != d.end(); it++) { if(!used[it->first]) { unordered_set<char> loop; bool l = topologicalSort(d, used, result, it->first, loop); if(l) return ""; } } for(auto i = used.begin(); i != used.end(); i++) { if(!i->second) result = i->first + result; } return result; } bool topologicalSort(unordered_map<char, vector<char>> d, unordered_map<char, bool>& used, string& result, char cur, unordered_set<char>& loop) { used[cur] = true; loop.insert(cur); for(auto i : d[cur]) { if(loop.find(i) != loop.end()) return true; if(!used[i]) { bool l = topologicalSort(d, used, result, i, loop); if(l) return true; } } result = cur + result; return false; } };
類似題目:
[LeetCode] 207. Course Schedule 課程安排
[LeetCode] 210. Course Schedule II 課程安排II