This page looks best with JavaScript enabled

Alien dictionary

 ·  ☕ 4 min read · 👀... views

Build a graph to solve alien dictionary problem via DFS/BFS.

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

Note:

  1. You may assume all letters are in lowercase.
  2. If the order is invalid, return an empty string ([“abcd”,“ab”] is invalid).
  3. There may be multiple valid order of letters, return any one of them is fine.

Method 1: DFS

Build the graph (post-adjacency list and visited list), then use DFS to build the correct order, while checking the loop at the same time.

Note:

  1. visited[]: -1(not exist), 0(no pre-node), 1(visiting), 2(visited)
  2. The order of adding char into stringbuilder is reversed: add post nodes to sb firstly in order to avoid missing pre-nodes for current nodes later. e.g.: for correct order “abc”, if meet ‘b’ firstly, build the sb as “cb”, and then meet ‘a’, build it as “cba”; otherwise, when meet ‘b’, build sb as “bc”, and then meet ‘a’, resulting in “bca”, which is incorrect.)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
    private static int N = 26;
    public String alienOrder(String[] words) {
        boolean[][] adj = new boolean[N][N];
        int[] visited = new int[N];
        if (!buildGraph(words, adj, visited)) return ""; // "abcd" -> "ab"

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < N; i++) {
            if (visited[i] == 0) {
                if (!dfs(adj, visited, sb, i)) return "";
            }
        }
        return sb.reverse().toString();
    }
    private boolean dfs(boolean[][] adj, int[] visited, StringBuilder sb, int i) {
        visited[i] = 1;  // visiting
        for (int j = 0; j < N; j++) {
            if (adj[i][j]) { // connected post nodes
                if (visited[j] == 1) return false;  // loop case
                if (visited[j] == 0) {
                    if (!dfs(adj, visited, sb, j)) return false;
                }
            }
        }
        visited[i] = 2; // visited
        sb.append((char)('a' + i));
        return true;
    }        
    private boolean buildGraph(String[] words, boolean[][] adj, int[] visited) {
        Arrays.fill(visited, -1); // init to not existed
        for (int i = 0; i < words.length; i++) {
            for (char c : words[i].toCharArray()) visited[c - 'a'] = 0;
            if (i > 0) {
                String w1 = words[i-1], w2 = words[i];
                int len = Math.min(w1.length(), w2.length()), j = 0;
                for (; j < len; j++) {
                    char c1 = w1.charAt(j), c2 = w2.charAt(j);
                    if (c1 != c2) {
                        adj[c1 - 'a'][c2 - 'a'] = true;
                        break;
                    }
                }
                if (j == len && w1.length() > w2.length()) return false; // "abcd" -> "ab"
            }
        }
        return true;
    }

Method 2: BFS

Build the graph(post-adjacency list and visited list), then use Karn’s algorithm to do topological sort (essentially BFS).

Note:

  1. visited[]: -1(not exist), 0(no pre-node), 1,2,3…(pre-nodes number)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
    public String alienOrder(String[] words) {
        List<Set<Integer>> adj = new ArrayList<>();
        for (int i = 0; i < 26; i++) adj.add(new HashSet<Integer>());
        int[] degree = new int[26];
        Arrays.fill(degree, -1);
        // init the adj and degree list
        for (int i = 0; i < words.length; i++) {
            for (char c : words[i].toCharArray()) {
                if (degree[c-'a'] < 0) degree[c-'a'] = 0;
            }
            if (i > 0) {
                String w1 = words[i-1], w2 = words[i];
                int len = Math.min(w1.length(), w2.length());
                for (int j = 0; j < len; j++) {
                    int c1 = w1.charAt(j) - 'a', c2 = w2.charAt(j) - 'a';
                    if (c1 != c2) {
                        if (!adj.get(c1).contains(c2)) {
                            adj.get(c1).add(c2);
                            degree[c2]++;
                        }
                        break;
                    }
                    if (j == len-1 && w1.length() > w2.length()) return ""; // "abcd" -> "ab"   
                }
            }
        }
        // topological sort
        Deque<Integer> q = new ArrayDeque<>();
        for (int i = 0; i < degree.length; i++) {
            if (degree[i] == 0) q.offer(i);
        }
        StringBuilder sb = new StringBuilder();
        while (!q.isEmpty()) {
            int i = q.poll();
            sb.append((char) ('a' + i));
            for (int j : adj.get(i)) {
                degree[j]--;
                if (degree[j] == 0) q.offer(j);
            }
        }
        for (int d : degree) if (d > 0) return ""; // has loop
        return sb.toString();
    }
Share on
Support the author with