|
| 1 | +# Time Complexity: O(N + K) - where N is total number of characters in all words, and K is total number of unique character relations (edges) we derive |
| 2 | +# Space Complexity: O(N + K) - for graph, in-degree map, and heap |
| 3 | + |
| 4 | +class Solution: |
| 5 | + def alien_order(self, words): |
| 6 | + # adjacency list, u -> set of v |
| 7 | + graph = defaultdict(set) |
| 8 | + # how many chars come before this one |
| 9 | + in_degree = {} |
| 10 | + |
| 11 | + # initialize in_degree with all unique chars |
| 12 | + for word in words: |
| 13 | + for char in word: |
| 14 | + in_degree[char] = 0 |
| 15 | + |
| 16 | + # compare each adjacent pair of words |
| 17 | + for i in range(len(words) - 1): |
| 18 | + w1, w2 = words[i], words[i + 1] |
| 19 | + min_len = min(len(w1), len(w2)) |
| 20 | + # handle invalid case like ["abc", "ab"] |
| 21 | + if len(w1) > len(w2) and w1[:min_len] == w2[:min_len]: |
| 22 | + return "" |
| 23 | + |
| 24 | + for j in range(min_len): |
| 25 | + if w1[j] != w2[j]: |
| 26 | + # first different char tells us the order |
| 27 | + if w2[j] not in graph[w1[j]]: |
| 28 | + graph[w1[j]].add(w2[j]) |
| 29 | + in_degree[w2[j]] += 1 |
| 30 | + # only first different char matters |
| 31 | + break |
| 32 | + |
| 33 | + # topological sort (use min heap for smallest lex order) |
| 34 | + heap = [] |
| 35 | + for char in in_degree: |
| 36 | + if in_degree[char] == 0: |
| 37 | + heapq.heappush(heap, char) |
| 38 | + |
| 39 | + result = [] |
| 40 | + while heap: |
| 41 | + char = heapq.heappop(heap) |
| 42 | + result.append(char) |
| 43 | + for neighbor in sorted(graph[char]): |
| 44 | + in_degree[neighbor] -= 1 |
| 45 | + if in_degree[neighbor] == 0: |
| 46 | + heapq.heappush(heap, neighbor) |
| 47 | + |
| 48 | + # if we added all characters into result, it's valid |
| 49 | + if len(result) == len(in_degree): |
| 50 | + return ''.join(result) |
| 51 | + else: |
| 52 | + return "" |
0 commit comments