Sunday, May 28, 2017

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 non-empty words from the dictionary, where words are sorted lexicographically by the rules of this new language. Derive the order of letters in this language.
Example 1:
Given the following words in dictionary,
[
  "wrt",
  "wrf",
  "er",
  "ett",
  "rftt"
]
The correct order is: "wertf".
Example 2:
Given the following words in dictionary,
[
  "z",
  "x"
]
The correct order is: "zx".
Example 3:
Given the following words in dictionary,
[
  "z",
  "x",
  "z"
]
The order is invalid, so return "".
Note:
  1. You may assume all letters are in lowercase.
  2. You may assume that if a is a prefix of b, then a must appear before b in the given dictionary.
  3. If the order is invalid, return an empty string.
  4. There may be multiple valid order of letters, return any one of them is fine.



Solution:

The idea is to use topological sort to get the order of characters.

To build the graph, we compare every two adjacent words.

If we found the character in the same index different, we know the order of these two characters, and thus add an edge and update the indegree.

After building the graph, we simply apply topological sort.



Code:


public class Solution {
    public String alienOrder(String[] words) {
        ArrayList<Character>[] adj = new ArrayList[26];
        int[] indegree = new int[26];
        Arrays.fill(indegree, -1);
        
        for (int i = 0; i < 26; i++) {
            adj[i] = new ArrayList<Character>();
        }
        
        for (int i = 0; i < words.length; i++) {
            for (char c : words[i].toCharArray()) {
                if (indegree[c - 'a'] < 0) {
                    indegree[c - 'a'] = 0;
                }
            }
            if (i == 0) {
                continue;
            }
            String w1 = words[i - 1];
            String w2 = words[i];
            int len = Math.min(w1.length(), w2.length());
            for (int j = 0; j < len; j++) {
                char c1 = w1.charAt(j);
                char c2 = w2.charAt(j);
                if (c1 == c2) {
                    if (j == w2.length() - 1 && w1.length() > w2.length()) {
                        return "";
                    }
                    continue;
                }
                if (!adj[c1 - 'a'].contains(c2)) {
                    adj[c1 - 'a'].add(c2);
                    indegree[c2 - 'a']++;
                }
                break;
            }
        }
        
        Queue<Character> queue = new LinkedList<>();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 26; i++) {
            if (indegree[i] == 0) {
                queue.offer((char)('a' + i));
            }
        }
        while (!queue.isEmpty()) {
            char c = queue.poll();
            sb.append(c);
            for (char nei : adj[c - 'a']) {
                indegree[nei - 'a']--;
                if (indegree[nei - 'a'] == 0) {
                    queue.offer(nei);
                }
            }
        }
        for (int d : indegree) {
            if (d > 0) {
                return "";
            }
        }
        return sb.toString();
    }
}



Use HashMap


public class Solution {
    public String alienOrder(String[] words) {
        if (words == null || words.length == 0) {
            return "";
        }
        StringBuilder order = new StringBuilder();
        HashMap<Character, HashSet<Character>> adj = new HashMap<>();
        for (char c = 'a'; c <= 'z'; c++) {
            adj.put(c, new HashSet<Character>());
        }
        int[] degree = new int[26];
        Arrays.fill(degree, -1);
        for (int i = 0; i < words.length; i++) {
            for (char c : words[i].toCharArray()) {
                if (degree[c - 'a'] == -1) {
                    degree[c - 'a'] = 0;
                }
            }
            if (i == 0) {
                continue;
            }
            String w1 = words[i - 1];
            String w2 = words[i];
            int len = Math.min(w1.length(), w2.length());
            for (int j = 0; j < len; j++) {
                char c1 = w1.charAt(j);
                char c2 = w2.charAt(j);
                if (c1 != c2) {
                    if (!adj.get(c1).contains(c2)) {
                        adj.get(c1).add(c2);
                        degree[c2 - 'a']++;  
                    }
                    break;
                }
                if (j == w2.length() - 1 && w1.length() > w2.length()) {
                    return "";
                }
            }
        }
        Queue<Character> queue = new LinkedList<>();
        for (int i = 0; i < 26; i++) {
            if (degree[i] == 0) {
                queue.offer((char)(i + 'a'));
            }
        }
        while (!queue.isEmpty()) {
            char c = queue.poll();
            order.append(c);
            for (char nei : adj.get(c)) {
                if (--degree[nei - 'a'] == 0) {
                    queue.offer(nei);
                }
            }
        }
        for (int i = 0; i < 26; i++) {
            if (degree[i] > 0) {
                return "";
            }
        }
        return order.toString();
    }
}