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".

分析

这种题很明显是用topological sort的典型题目。无非是两步,建图 + DFS。

至于怎么建图,由于这里的单词是按顺序排列的,所以对于两个连续的单词,我们可以找到他们第一个不相同的字符,就可以得到一个字符的相对顺序。根据得到的一系列字符的相对顺序,可以构造出图,后面就是普通的topological sort的步骤了。

复杂度

time: 建图->O(n*k), Topological sort-> O(26 + n) = O(n)
space: O(n),主要是Map的大小
k表示单词平均长度

代码

public class Solution {
    public String alienOrder(String[] words) {
        Map<Character, List<Character>> graph = new HashMap<Character, List<Character>>();
        for (int i = 0; i < words.length; i++) {
            for (int j = 0; j < words[i].length(); j++) {
                char c = words[i].charAt(j);
                if (!graph.containsKey(c)) {                      
                    graph.put(c, new ArrayList<Character>()); // 为了排序结果出现所有字母,对于每个字母,都要初始化,
                }
            }
            
            // 根据连续的两个单词,得到一个相对的字符顺序
            if (i > 0) {
                check(words[i - 1], words[i], graph);
            }
        }
        
        Stack<Character> stack = new Stack<Character>();
        boolean[] visited = new boolean[26]; // 标记DFS路径上的点
        boolean[] isLoop = new boolean[26]; // 用来发现环
        for (char c : graph.keySet()) {
            
            // 有环,无效输入,返回空字符串
            if (!dfs(graph, c, visited, isLoop, stack)) 
                return "";
        }
        StringBuilder sb = new StringBuilder();
        
        // 得到最终正确的字母顺序
        while (!stack.isEmpty()) 
            sb.append(stack.pop());
        return sb.toString();
    }
    
    private boolean dfs(Map<Character, List<Character>> graph, char c, boolean[] visited, boolean[] isLoop, Stack<Character> stack) {
        int i = c - 'a';
        if (visited[i]) return true; // 已经搜过了,可以直接跳过
        if (isLoop[i]) return false; // 发现环
        
        isLoop[i] = true;
        for (char next : graph.get(c)) {
            if (!dfs(graph, next, visited, isLoop, stack)) 
                return false;
        }
        visited[i] = true;
        stack.push(c);
        return true;
    }
    
    public void check(String word1, String word2, Map<Character, List<Character>> map) {
        int i = 0;
        
        // 找到两者第一个不相等的字符
        while (i < word1.length() && i < word2.length() && word1.charAt(i) == word2.charAt(i)) 
            i++; 
        if (i < word1.length() && i < word2.length()) 
            map.get(word1.charAt(i)).add(word2.charAt(i));
    }
}

微斯渝
39 声望15 粉丝