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:
- You may assume all letters are in lowercase.
- If the order is invalid, return an empty string.
- There may be multiple valid order of letters, return any one of them is fine.
这道题让给了我们一些按“字母顺序”排列的单词,但是这个字母顺序不是我们熟知的顺序,而是另类的顺序,让我们根据这些“有序”的单词来找出新的字母顺序,这实际上是一道有向图的问题,跟之前的那两道Course Schedule II和Course Schedule的解法很类似,我们先来看BFS的解法,我们需要一个set来保存我们可以推测出来的顺序关系,比如题目中给的例子,我们可以推出的顺序关系有:
t->f w->e r->t e->r
那么set就用来保存这些pair,我们还需要另一个set来保存所有出现过的字母,需要一个一维数组in来保存每个字母的入度,另外还要一个queue来辅助拓扑遍历,我们先遍历单词集,把所有字母先存入ch,然后我们每两个相邻的单词比较,找出顺序pair,然后我们根据这些pair来赋度,我们把ch中入度为0的字母都排入queue中,然后开始遍历,如果字母在set中存在,则将其pair中对应的字母的入度减1,若此时入度减为0了,则将对应的字母排入queue中并且加入结果res中,直到遍历完成,我们看结果res和ch中的元素个数是否相同,若不相同则说明可能有环存在,返回空字符串,参见代码如下:
解法一:
class Solution { public: string alienOrder(vector<string>& words) { set<pair<char, char> > s; unordered_set<char> ch; vector<int> in(256, 0); queue<char> q; string res = ""; for (auto a : words) ch.insert(a.begin(), a.end()); for (int i = 0; i < words.size() - 1; ++i) { int mn = min(words[i].size(), words[i + 1].size()), j = 0; for (; j < min(words[i].size(), words[i + 1].size()); ++j) { if (words[i][j] != words[i + 1][j]) { s.insert(make_pair(words[i][j], words[i + 1][j])); break; } } if (j == mn && words[i].size() > words[i + 1].size()) return ""; } for (auto a : s) ++in[a.second]; for (auto a : ch) { if (in[a] == 0) { q.push(a); res += a; } } while (!q.empty()) { char c = q.front(); q.pop(); for (auto a : s) { if (a.first == c) { --in[a.second]; if (in[a.second] == 0) { q.push(a.second); res += a.second; } } } } return res.size() == ch.size() ? res : ""; } };
下面来看一种DFS的解法,思路和BFS的很类似,我们需要建立一个二维的bool数组g,然后把出现过的字母的对应的位置都赋为true,然后我们把可以推出的顺序的对应位置也赋为true,然后我们就开始递归调用,如果递归函数有返回false的,说明有冲突,则返回false,递归调用结束后结果res中存了入度不为0的字母,最后把入度为0的字母加到最后面,最后把结果res翻转一下即可,参见代码如下:
解法二:
class Solution { public: string alienOrder(vector<string>& words) { vector<vector<bool>> g(26, vector<bool>(26, false)); vector<bool> path(26, false); string res = ""; for (string word : words) { for (char c : word) { g[c - 'a'][c - 'a'] = true; } } for (int i = 0; i < words.size() - 1; ++i) { int mn = min(words[i].size(), words[i + 1].size()), j = 0; for (; j < mn; ++j) { if (words[i][j] != words[i + 1][j]) { g[words[i][j] - 'a'][words[i + 1][j] - 'a'] = true; break; } } if (j == mn && words[i].size() > words[i + 1].size()) return ""; } for (int i = 0; i < 26; ++i) { if (!dfs(g, i, path, res)) return ""; } for (int i = 0; i < 26; ++i) { if (g[i][i]) res += (char)(i + 'a'); } return string(res.rbegin(), res.rend()); } bool dfs(vector<vector<bool>> &g, int idx, vector<bool> &path, string &res) { if (!g[idx][idx]) return true; path[idx] = true; for (int i = 0; i < 26; ++i) { if (i == idx || !g[idx][i]) continue; if (path[i]) return false; if (!dfs(g, i, path, res)) return false; } path[idx] = false; g[idx][idx] = false; res += (char)(idx + 'a'); return true; } };
本文转自博客园Grandyang的博客,原文链接:另类字典[LeetCode] Alien Dictionary ,如需转载请自行联系原博主。