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]
    
    

All Problems

All Solutions