Question
Formatted question description: https://leetcode.ca/all/269.html
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 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".
Input:
[
"z",
"x",
"z"
]
Output: ""
Explanation: The order is invalid, so return "".
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.
@tag-graph
Algorithm
It is actually a problem of directed graph traversal
.
For each node in the directed graph, calculate its in-degree, and then start BFS
traversing the directed graph from the node with an in-degree of 0
, and then save the traversal path and return.
Code
Java
-
import java.util.*; public class Alien_Dictionary { public static void main(String[] args) { Alien_Dictionary out = new Alien_Dictionary(); Solution s = out.new Solution(); Solution_bfs s2 = out.new Solution_bfs(); String[] words = new String[] { "wrt", "wrf", "er", "ett", "rftt" }; System.out.println(s.alienOrder(words)); System.out.println(s2.alienOrder(words)); } // ref: http://buttercola.blogspot.com/2015/09/leetcode-alien-dictionary.html public class Solution_bfs { /** * @param words: a list of words * @return: a string which is correct order */ public String alienOrder(String[] words) { // Write your code here if (words == null || words.length == 0) { return ""; } // step 1: construct the graph // Map<Character, List<Character>> adjMap = new HashMap<>(); constructGraph(words, adjMap); int numNodes = adjMap.size(); StringBuilder result = new StringBuilder(); // toplogical sorting // Map<Character, Integer> indegreeMap = new HashMap<>(); for (Character node : adjMap.keySet()) { indegreeMap.put(node, 0); } for (Character node : adjMap.keySet()) { for (Character neighbor : adjMap.get(node)) { int indegree = indegreeMap.get(neighbor); indegree += 1; indegreeMap.put(neighbor, indegree); } } // start from indegree=0 Queue<Character> queue = new PriorityQueue<>(); for (Character node : indegreeMap.keySet()) { if (indegreeMap.get(node) == 0) { // starting node, can only be one, cannot be 2 starting with 0 indegree queue.offer(node); } } while (!queue.isEmpty()) { char curNode = queue.poll(); result.append(curNode); for (char neighbor : adjMap.get(curNode)) { int indegree = indegreeMap.get(neighbor); indegree -= 1; indegreeMap.put(neighbor, indegree); // @note: key is here. // for A->B, B->C, A-C: C will not be counted until its indgree is 0 if (indegree == 0) { queue.offer(neighbor); } } } if (result.length() < numNodes) { return ""; } return result.toString(); } private void constructGraph(String[] words, Map<Character, List<Character>> adjMap) { // construct nodes for (String word : words) { for (Character c : word.toCharArray()) { adjMap.put(c, new ArrayList<>()); // c to all its next } } // construct edges for (int i = 1; i < words.length; i++) { String prev = words[i - 1]; String curr = words[i]; for (int j = 0; j < prev.length() && j < curr.length(); j++) { if (prev.charAt(j) != curr.charAt(j)) { adjMap.get(prev.charAt(j)).add(curr.charAt(j)); break; } } } } } public class Solution { // dfs public String alienOrder(String[] words) { // Step 1: build the graph Map<Character, Set<Character>> graph = new HashMap<>(); for (int i = 0; i < words.length; i++) { String currentWord = words[i]; for (int j = 0; j < currentWord.length(); j++) { if (!graph.containsKey(currentWord.charAt(j))) { graph.put(currentWord.charAt(j), new HashSet<>()); } } if (i > 0) { connectGraph(graph, words[i - 1], currentWord); } } // Step 2: topological sorting StringBuffer sb = new StringBuffer(); Map<Character, Integer> visited = new HashMap<Character, Integer>(); // mark as visited: visited.put(vertexId, -1); for (Map.Entry<Character, Set<Character>> entry: graph.entrySet()) { char vertexId = entry.getKey(); if (!topologicalSort(vertexId, graph, sb, visited)) { return ""; } } return sb.toString(); } private void connectGraph(Map<Character, Set<Character>> graph, String prev, String curr) { if (prev == null || curr == null) { return; } int len = Math.min(prev.length(), curr.length()); for (int i = 0; i < len; i++) { char p = prev.charAt(i); char q = curr.charAt(i); if (p != q) { // so if same duplicated work, will not reach here and not update graph if (!graph.get(p).contains(q)) { graph.get(p).add(q); } break; } } } private boolean topologicalSort( char vertexId, Map<Character, Set<Character>> graph, StringBuffer sb, Map<Character, Integer> visited ) { if (visited.containsKey(vertexId)) { // visited if (visited.get(vertexId) == -1) { // -1 meaning visited, cycle found return false; } // already in the list if (visited.get(vertexId) == 1) { return true; } } visited.put(vertexId, -1); // mark as visited Set<Character> neighbors = graph.get(vertexId); for (char neighbor : neighbors) { if (!topologicalSort(neighbor, graph, sb, visited)) { return false; } } sb.insert(0, vertexId); visited.put(vertexId, 1); // restore visited return true; } } }
-
// OJ: https://leetcode.com/problems/alien-dictionary/ // Time: O(N) // Space: O(1) class Solution { public: string alienOrder(vector<string>& A) { unordered_map<int, unordered_set<int>> G; int indegree[26] = {}; for (auto &s : A) { for (char c : s) G[c - 'a'] = {}; } for (int i = 1; i < A.size(); ++i) { int j = 0; for (; j < min(A[i - 1].size(), A[i].size()); ++j) { if (A[i - 1][j] == A[i][j]) continue; G[A[i - 1][j] - 'a'].insert(A[i][j] - 'a'); break; } if (j == A[i].size() && j < A[i - 1].size()) return ""; } for (auto &[from, tos] : G) { for (int to : tos) { indegree[to]++; } } queue<int> q; for (int i = 0; i < 26; ++i) { if (G.count(i) && indegree[i] == 0) q.push(i); } string ans; while (q.size()) { int u = q.front(); q.pop(); ans += u + 'a'; for (int v : G[u]) { if (--indegree[v] == 0) q.push(v); } } return ans.size() == G.size() ? ans : ""; } };
-
import collections class Node(object): def __init__(self, val): self.val = val self.neighbors = [] def connect(self, node): self.neighbors.append(node) def getNbrs(self): return self.neighbors class Solution(object): def alienOrder(self, words): """ :type words: List[str] :rtype: str """ def dfs(root, graph, visited): visited[root] = 1 for nbr in graph[root].getNbrs(): if visited[nbr.val] == 0: if not dfs(nbr.val, graph, visited): return False elif visited[nbr.val] == 1: return False visited[root] = 2 self.ans += root return True self.ans = "" graph = {} visited = collections.defaultdict(int) self.topNum = 0 for i in range(0, len(words) - 1): a = words[i] b = words[i + 1] i = 0 while i < len(a) and i < len(b): if a[i] != b[i]: nodeA = nodeB = None if a[i] not in graph: nodeA = Node(a[i]) graph[a[i]] = nodeA else: nodeA = graph[a[i]] if b[i] not in graph: nodeB = Node(b[i]) graph[b[i]] = nodeB else: nodeB = graph[b[i]] nodeA.connect(nodeB) break i += 1 if i < len(a) and i >= len(b): return "" for c in graph: if visited[c] == 0: if not dfs(c, graph, visited): return "" unUsedSet = set() for word in words: for c in word: unUsedSet.add(c) for c in unUsedSet: if c not in graph: self.ans += c return self.ans[::-1]