LeetCode 269. Alien Dictionary(外星人字典)

时间:2021-08-01 22:25:46

原题网址:https://leetcode.com/problems/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, wherewords 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.
  3. There may be multiple valid order of letters, return any one of them is fine.
思路:

(1)如何生成图?前后两个单词的逐个字母检查,这个方法实现起来最简单。

(2)如何生成字母表?可以用深度优先搜索或者拓扑排序。


方法一:图的深度优先搜索。深度优先的关键点在于如何检查环路,使用visited=0/1/2而不是布尔类型可以解决,即visited=0表示未访问UNVISITED,1表示访问中VISITING,2表示已访问VISITED。另外,深度优先搜索的话,graph用入边来表示,graph[i][j] = true <=> j->i,这样就容易通过递归方式,先解决所依赖的节点。

public class Solution {
private void find(boolean[] alphabets, boolean[][] graph, int tail, int[] visited, StringBuilder sb) {
if (!alphabets[tail] || visited[tail] != 0) return;
visited[tail] = 1;
for(int i=0; i<graph[tail].length; i++) {
if (graph[tail][i] && alphabets[i] && visited[i] == 1) return;
if (graph[tail][i] && alphabets[i] && visited[i] == 0) find(alphabets, graph, i, visited, sb);
}
visited[tail] = 2;
sb.append((char)(tail+'a'));
}
public String alienOrder(String[] words) {
char[][] ws = new char[words.length][];
boolean[] alphabets = new boolean[26];
int letters = 0;
for(int i=0; i<words.length; i++) {
ws[i] = words[i].toCharArray();
for(int j=0; j<ws[i].length; j++) {
if (!alphabets[ws[i][j]-'a']) {
alphabets[ws[i][j]-'a'] = true;
letters ++;
}
}
}
boolean[][] graph = new boolean[26][26];
for(int i=0; i<words.length-1; i++) {
for(int j=0; j<Math.min(words[i].length(), words[i+1].length()); j++) {
if (ws[i+1][j] != ws[i][j]) {
graph[ws[i+1][j]-'a'][ws[i][j]-'a'] = true;
break;
}
}
}
int[] visited = new int[26];
StringBuilder sb = new StringBuilder();
for(int i=0; i<alphabets.length; i++) {
if (!alphabets[i] || visited[i]!=0) continue;
find(alphabets, graph, i, visited, sb);
}
// System.out.println(sb.toString());
if (sb.length() == letters) return sb.toString(); else return "";
}
}


方法二:拓扑排序。需要检查判断无法再继续生成字母表的情况(环路),如果使用出边来表示graph,即graph[i][j] = true <=> i-->j,则需要另外再辅助入度的变量indegrees。

public class Solution {
public String alienOrder(String[] words) {
boolean[][] graph = new boolean[26][26];
int[] indegrees = new int[26];
boolean[] alphabets = new boolean[26];
int alphabetsCount = 0;
char[] word =new char[0];
for(int i=0; i<words.length; i++) {
char[] prev = word;
word = words[i].toCharArray();
for(int j=0; j<word.length; j++) {
if (!alphabets[word[j]-'a']) {
alphabets[word[j]-'a'] = true;
alphabetsCount ++;
}
}
for(int j=0; j<Math.min(prev.length, word.length); j++) {
if (prev[j] != word[j]) {
if (!graph[prev[j]-'a'][word[j]-'a']) {
graph[prev[j]-'a'][word[j]-'a'] = true;
indegrees[word[j]-'a'] ++;
}
break;
}
}
}
char[] result = new char[alphabetsCount];
int pos = 0;
do {
boolean changed = false;
for(int i=0; i<alphabets.length; i++) {
if (alphabets[i]) {
if (indegrees[i] == 0) {
result[pos++] = (char)(i+'a');
changed = true;
for(int j=0; j<graph[i].length; j++) {
if (graph[i][j]) {
indegrees[j] --;
}
}
alphabets[i] = false;
}
}
}
if (!changed) break;
} while (pos < result.length);
return pos == result.length ? new String(result) : "";
}
}