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));
    }


    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;
        }
    }

    // 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;
                    }
                }
            }
        }
    }
}

All Problems

All Solutions