题目
现有一种使用英语字母的外星文语言,这门语言的字母顺序与英语顺序不同。
给定一个字符串列表 words ,作为这门语言的词典,words 中的字符串已经 按这门新语言的字母顺序进行了排序 。
请你根据该词典还原出此语言中已知的字母顺序,并 按字母递增顺序 排列。若不存在合法字母顺序,返回 “” 。若存在多种可能的合法字母顺序,返回其中 任意一种 顺序即可。
字符串 s 字典顺序小于 字符串 t 有两种情况:
在第一个不同字母处,如果 s 中的字母在这门外星语言的字母顺序中位于 t 中字母之前,那么 s 的字典顺序小于 t 。
如果前面 min(s.length, t.length) 字母都相同,那么 s.length < t.length 时,s 的字典顺序也小于 t 。示例 1:
输入:words = [“wrt”,”wrf”,”er”,”ett”,”rftt”]
输出:”wertf”示例 2:
输入:words = [“z”,”x”]
输出:”zx”示例 3:
输入:words = [“z”,”x”,”z”]
输出:””
解释:不存在合法字母顺序,因此返回 “” 。提示:
1 <= words.length <= 100
1 <= words[i].length <= 100
words[i] 仅由小写英文字母组成来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/Jf1JuT
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
虽然是hard难度,但思路比较好想。
对于输入的单词数组,对于相邻的两个字符串两两比较,得到关于字母的先后顺序,形成一个图,规定较小的字母到较大的字母有一条「有向」的边。
建好图之后,使用拓扑排序,输出可能的字母先后顺序,并验证是否有环,如果有环返回空串,没有的话返回生成的字母顺序。
不过细节较多,见注释。
代码
class Solution {
public String alienOrder(String[] words) {
int n = words.length;
int[] inDegree = new int[26];
// 初始化为-1,表示该字符不存在
Arrays.fill(inDegree, -1);
// 出现过的字符种类
int cnt = 0;
for (String word : words) {
for (char c : word.toCharArray()) {
if (inDegree[c - 'a'] == -1) {
cnt++;
}
inDegree[c - 'a'] = 0;
}
}
// key为一个字符,val为比其小的所有字符
// 这里使用set去重,不过好像用list也可以(如果发生了重复一定有环)
Map<Character, Set<Character>> graph = new HashMap<>();
// 比较相邻的两个单词,以生成字符先后顺序的图
for (int i = 1; i < n; i++) {
int p = 0;
int q = 0;
int len1 = words[i - 1].length();
int len2 = words[i].length();
// 找第一个不相同的字母
while (p < len1 && q < len2 && words[i - 1].charAt(p) == words[i].charAt(q)) {
p++;
q++;
}
// 后面的字符串遍历完了,前面的字符串还剩余字母,例如abcd和abc,其实这种用例不符合条件不应该出现
if (p < len1 && q == len2) {
return "";
} else if (p < len1 && q < len2) {
// 都剩余字母没遍历完,当前p和q指向的字符就是不同的,例如abd和abc,那么就建立一条d到c的边,表示d比c小
char c1 = words[i - 1].charAt(p);
char c2 = words[i].charAt(q);
boolean res = graph.computeIfAbsent(c1, k -> new HashSet<>()).add(c2);
// 只有c1的set中第一次添加c2,才增加c2的入度
if (res) {
inDegree[c2 - 'a']++;
}
}
}
// 剩下就是拓扑排序基本写法,这里是BFS
Queue<Character> q = new LinkedList<>();
for (int i = 0; i < 26; i++) {
if (inDegree[i] == 0) {
q.offer((char) (i + 'a'));
}
}
StringBuilder sb = new StringBuilder();
while (!q.isEmpty()) {
char cur = q.poll();
sb.append(cur);
Set<Character> l = graph.get(cur);
// 注意cur可能不存在比其大的字母
if (l == null) {
continue;
}
for (char c : l) {
inDegree[c - 'a']--;
if (inDegree[c - 'a'] == 0) {
q.offer(c);
}
}
}
// 如果sb长度小于字符种类,一定是有环
return sb.length() == cnt ? sb.toString() : "";
}
}