|
| 1 | +# Time: O(|V|+|E|) = O(26 + 26^2) = O(1) |
| 2 | +# Space: O(|E|) = O(26^2) = O(1) |
| 3 | + |
| 4 | +class Solution(object): |
| 5 | + def alienOrder(self, words): |
| 6 | + """ |
| 7 | + :type words: List[str] |
| 8 | + :rtype: str |
| 9 | + """ |
| 10 | + # Find ancestors of each node by DFS |
| 11 | + nodes, ancestors = {}, {} |
| 12 | + for i in xrange(len(words)): |
| 13 | + for c in words[i]: |
| 14 | + nodes[c] = True |
| 15 | + |
| 16 | + for node in nodes.keys(): |
| 17 | + ancestors[node] = [] |
| 18 | + |
| 19 | + for i in xrange(1, len(words)): |
| 20 | + self.findEdges(words[i - 1], words[i], ancestors) |
| 21 | + |
| 22 | + # Output topological order by DFS |
| 23 | + result = [] |
| 24 | + visited = {} |
| 25 | + for node in nodes.keys(): |
| 26 | + if self.topSortDFS(node, node, ancestors, visited, result): |
| 27 | + return "" |
| 28 | + |
| 29 | + return "".join(result) |
| 30 | + |
| 31 | + |
| 32 | + # Construct the graph. |
| 33 | + def findEdges(self, word1, word2, ancestors): |
| 34 | + str_len = min(len(word1), len(word2)) |
| 35 | + for i in xrange(str_len): |
| 36 | + if word1[i] != word2[i]: |
| 37 | + ancestors[word2[i]].append(word1[i]) |
| 38 | + break |
| 39 | + |
| 40 | + |
| 41 | + # Topological sort, return whether there is a cycle. |
| 42 | + def topSortDFS(self, root, node, ancestors, visited, result): |
| 43 | + if node not in visited: |
| 44 | + visited[node] = root |
| 45 | + for ancestor in ancestors[node]: |
| 46 | + if self.topSortDFS(root, ancestor, ancestors, visited, result): |
| 47 | + return True |
| 48 | + result.append(node) |
| 49 | + elif visited[node] == root: |
| 50 | + # Visited from the same root in the DFS path. |
| 51 | + # So it is cyclic. |
| 52 | + return True |
| 53 | + return False |
0 commit comments