https://zhuhan0.blogspot.com/2017/06/leetcode-269-alien-dictionary.html
X. DFS
https://github.com/awangdev/LintCode/blob/master/Java/Alien%20Dictionary.java
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,
Given the following words in dictionary,
[ "wrt", "wrf", "er", "ett", "rftt" ]
The correct order is:
"wertf"
.
Example 2:
Given the following words in dictionary,
Given the following words in dictionary,
[ "z", "x" ]
The correct order is:
"zx"
.
Example 3:
Given the following words in dictionary,
Given the following words in dictionary,
[ "z", "x", "z" ]
The order is invalid, so return
""
.
Note:
- You may assume all letters are in lowercase.
- You may assume that if a is a prefix of b, then a must appear before b in the given dictionary.
- If the order is invalid, return an empty string.
- There may be multiple valid order of letters, return any one of them is fine.
Topological sort:
- Build graph:
- a map of character -> set of character.
- Also get in-degrees for each character. In-degrees will be a map of character -> integer.
- Topological sort:
- Loop through in-degrees. Offer the characters with in-degree of 0 to queue.
- While queue is not empty:
- Poll from queue. Append to character to result string.
- Decrease the in-degree of polled character's children by 1.
- If any child's in-degree decreases to 0, offer it to queue.
- At last, if result string's length is less than the number of vertices, that means there is a cycle in my graph. The order is invalid.
Say the number of characters in the dictionary (including duplicates) is n. Building the graph takes O(n). Topological sort takes O(V + E). V <= n. E also can't be larger than n. So the overall time complexity is O(n).
public String alienOrder(String[] words) { Map<Character, Set<Character>> graph = new HashMap<>(); int[] inDegree = new int[26]; buildGraph(words, graph, inDegree); String order = topologicalSort(graph, inDegree); return order.length() == graph.size() ? order : ""; } private void buildGraph(String[] words, Map<Character, Set<Character>> graph, int[] inDegree) { for (String word : words) { for (char c : word.toCharArray()) { graph.put(c, new HashSet<>()); } } for (int i = 1; i < words.length; i++) { String first = words[i - 1]; String second = words[i]; int length = Math.min(first.length(), second.length()); for (int j = 0; j < length; j++) { char parent = first.charAt(j); char child = second.charAt(j); if (parent != child) { if (!graph.get(parent).contains(child)) { graph.get(parent).add(child); inDegree[child - 'a']++; } break; } } } } private String topologicalSort(Map<Character, Set<Character>> graph, int[] inDegree) { Queue<Character> queue = new LinkedList<>(); for (char c : graph.keySet()) { if (inDegree[c - 'a'] == 0) { queue.offer(c); } } StringBuilder sb = new StringBuilder(); while (!queue.isEmpty()) { char c = queue.poll(); sb.append(c); for (char neighbor : graph.get(c)) { inDegree[neighbor - 'a']--; if (inDegree[neighbor - 'a'] == 0) { queue.offer(neighbor); } } } return sb.toString(); }
https://github.com/awangdev/LintCode/blob/master/Java/Alien%20Dictionary.java
- 本质: 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先
- 把 string array 变成topological sort的 graph: `map<char, list<char>>`
- 也可以`List[26] edges` (Course Schedule problem)
- Build edges: find char diff between two row, and store the order indication into graph
- 注意: indegree 永远是反向的 (跟 node to neighbors 相反的方式建立)
#### BFS
- topological sort 本身很好写, 但是要在题目中先了解到字母排序的本质
- 其实上面这个排序的本质很好想, 但是把它具体化成构建graph的代码, 会稍微有点难想到
- 算indegree, 然后用 BFS 来找到那些 inDegree == 0的 node
- 最先inDegree == 0的node, 就排在字母表前面.
- 下面的解法, 用了Graph: map<Character, List<Character>>, 而不是 List[26], 其实更加试用超过26个字母的dictionary.
- 如果 `inDegree.size() != result.length()`, there is nodes that did not make it into result.
- ex: cycle nodes from input, where inDegree of a one node would never reduce to 0, and will not be added to result
- In this case, it will be treated as invalid input, and return ""
#### DFS
- 跟BFS建立 grpah 的过程一模一样
- DFS的不同在于: 用visited map 来标记走过的地方
- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add)
DFS, mark visited. When dfs down to an leaf element,
it'll be the last element of the final output. (reverse order)
*/
Map<Character, List<Character>> graph = new HashMap<>();
Map<Character, Integer> visited = new HashMap<>();
StringBuffer sb = new StringBuffer();
public String alienOrder(String[] words) {
if (words == null || words.length == 0) {
return "";
}
// Build graph, and visited map.
buildGraph(words);
// Topological sort with dfs
for (char c : graph.keySet()) {
if (!dfs(c)) {
return "";
}
}
return sb.toString();
}
private boolean dfs(Character c) {
if (visited.get(c) == 1) return true;
if (visited.get(c) == -1) return false;
visited.put(c, -1);
for (char edgeNode : graph.get(c)) {
if (!dfs(edgeNode)) {
return false;
}
}
visited.put(c, 1);
sb.insert(0, c); // leaf element, add to buffer
return true;
}
private void buildGraph (String[] words) {
// Create nodes
for (String word: words) {
for (char c : word.toCharArray()) {
if (!graph.containsKey(c)) {
graph.put(c, new ArrayList<>());
visited.put(c, 0);
}
}
}
// Build edges
for (int i = 0; i < words.length - 1; i++) {
int index = 0;
while (index < words[i].length() && index < words[i + 1].length()) {
char c1 = words[i].charAt(index);
char c2 = words[i + 1].charAt(index);
if (c1 != c2) {
graph.get(c1).add(c2);
break;
}
index++;
}
}
}