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:

Input:
[
"wrt",
"wrf",
"er",
"ett",
"rftt"
] Output: "wertf"

Example 2:

Input:
[
"z",
"x"
] Output: "zx"

Example 3:

Input:
[
"z",
"x",
"z"
] Output: ""  Explanation: The order is invalid, so return "".

Note:

  1. You may assume all letters are in lowercase.
  2. You may assume that if a is a prefix of b, then a must appear before b in the given dictionary.
  3. If the order is invalid, return an empty string.
  4. There may be multiple valid order of letters, return any one of them is fine.

这道题让给了一些按“字母顺序”排列的单词,但是这个字母顺序不是我们熟知的顺序,而是另类的顺序,让根据这些“有序”的单词来找出新的字母顺序,这实际上是一道有向图遍历的问题,跟之前的那两道 Course Schedule II 和 Course Schedule 的解法很类似,我们先来看 BFS 的解法,需要一个 TreeSet 来保存可以推测出来的顺序关系,比如题目中给的例子1,可以推出的顺序关系有:

t->f
w->e
r->t
e->r

这些就是有向图的边,对于有向图中的每个结点,计算其入度,然后从入度为0的结点开始 BFS 遍历这个有向图,然后将遍历路径保存下来返回即可。下面来看具体的做法:

根据之前讲解,需用 TreeSet 来保存这些 pair,还需要一个 HashSet 来保存所有出现过的字母,需要一个一维数组 in 来保存每个字母的入度,另外还要一个 queue 来辅助拓扑遍历,我们先遍历单词集,把所有字母先存入 HashSet,然后我们每两个相邻的单词比较,找出顺序 pair,然后根据这些 pair 来赋度,把 HashSet 中入度为0的字母都排入 queue 中,然后开始遍历,如果字母在 TreeSet 中存在,则将其 pair 中对应的字母的入度减1,若此时入度减为0了,则将对应的字母排入 queue 中并且加入结果 res 中,直到遍历完成,看结果 res 和 ch 中的元素个数是否相同,若不相同则说明可能有环存在,返回空字符串,参见代码如下:

解法一:

class Solution {
public:
string alienOrder(vector<string>& words) {
set<pair<char, char>> st;
unordered_set<char> ch;
vector<int> in();
queue<char> q;
string res;
for (auto a : words) ch.insert(a.begin(), a.end());
for (int i = ; i < (int)words.size() - ; ++i) {
int mn = min(words[i].size(), words[i + ].size()), j = ;
for (; j < mn; ++j) {
if (words[i][j] != words[i + ][j]) {
st.insert(make_pair(words[i][j], words[i + ][j]));
break;
}
}
if (j == mn && words[i].size() > words[i + ].size()) return "";
}
for (auto a : st) ++in[a.second];
for (auto a : ch) {
if (in[a] == ) {
q.push(a);
res += a;
}
}
while (!q.empty()) {
char c = q.front(); q.pop();
for (auto a : st) {
if (a.first == c) {
--in[a.second];
if (in[a.second] == ) {
q.push(a.second);
res += a.second;
}
}
}
}
return res.size() == ch.size() ? res : "";
}
};

下面来看一种 DFS 的解法,思路和 BFS 的很类似,需要建立一个二维的 bool 数组g,为了节省空间,不必像上面的解法中一样使用一个 HashSet 来记录所有出现过的字母,可以直接用这个二维数组来保存这个信息,只要 g[i][i] = true,即表示位置为i的字母存在。同时,这个二维数组还可以保存顺序对儿的信息,只要 g[i][j] = true,就知道位置为i的字母顺序在位置为j的字母前面。找顺序对儿的方法跟上面的解法完全相同,之后就可以进行 DFS 遍历了。由于 DFS 遍历需要标记遍历结点,那么就用一个 visited 数组,由于是深度优先的遍历,并不需要一定要从入度为0的结点开始遍历,而是从任意一个结点开始都可以,DFS 会遍历到出度为0的结点为止,加入结果 res,然后回溯加上整条路径到结果 res 即可,参见代码如下:

解法二:

class Solution {
public:
string alienOrder(vector<string>& words) {
vector<vector<bool>> g(, vector<bool>());
vector<bool> visited();
string res;
for (string word : words) {
for (char c : word) {
g[c - 'a'][c - 'a'] = true;
}
}
for (int i = ; i < (int)words.size() - ; ++i) {
int mn = min(words[i].size(), words[i + ].size()), j = ;
for (; j < mn; ++j) {
if (words[i][j] != words[i + ][j]) {
g[words[i][j] - 'a'][words[i + ][j] - 'a'] = true;
break;
}
}
if (j == mn && words[i].size() > words[i + ].size()) return "";
}
for (int i = ; i < ; ++i) {
if (!dfs(g, i, visited, res)) return "";
}
return res;
}
bool dfs(vector<vector<bool>>& g, int idx, vector<bool>& visited, string& res) {
if (!g[idx][idx]) return true;
visited[idx] = true;
for (int i = ; i < ; ++i) {
if (i == idx || !g[i][idx]) continue;
if (visited[i]) return false;
if (!dfs(g, i, visited, res)) return false;
}
visited[idx] = false;
g[idx][idx] = false;
res += 'a' + idx;
return true;
}
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/269

类似题目:
 

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] 269. Alien Dictionary 另类字典的更多相关文章

  1. [LeetCode] 269. Alien Dictionary 外文字典

    There is a new alien language which uses the latin alphabet. However, the order among letters are un ...

  2. 269. Alien Dictionary 另类字典 *HARD*

    There is a new alien language which uses the latin alphabet. However, the order among letters are un ...

  3. [LeetCode] Alien Dictionary 另类字典

    There is a new alien language which uses the latin alphabet. However, the order among letters are un ...

  4. [leetcode]269. Alien Dictionary外星字典

    There is a new alien language which uses the latin alphabet. However, the order among letters are un ...

  5. LeetCode 269. Alien Dictionary

    原题链接在这里:https://leetcode.com/problems/alien-dictionary/ 题目: There is a new alien language which uses ...

  6. 269. Alien Dictionary火星语字典(拓扑排序)

    [抄题]: There is a new alien language which uses the latin alphabet. However, the order among letters ...

  7. 269. Alien Dictionary

    题目: There is a new alien language which uses the latin alphabet. However, the order among letters ar ...

  8. [Locked] Alien Dictionary

    Alien Dictionary There is a new alien language which uses the latin alphabet. However, the order amo ...

  9. 设计一个 硬件 实现的 Dictionary(字典)

    Dictionary 就是 字典, 是一种可以根据 Key 来 快速 查找 Value 的 数据结构 . 比如 我们在 C# 里用到的 Dictionary<T>, 在 程序设计 里, 字 ...

随机推荐

  1. Shell基本运算符之算术、关系运算符

    Shell 运算符 =============================摘自菜鸟教程================================= Shell和其他编程语言一样,支持多种运算 ...

  2. 此贴告诉你:为啥shell脚本人,不建议学python

    py很强大,我承认.但在运维方面,py不但不强大,还有硬伤.正因为有下述硬伤,所以我们运维,还是用shell多,用py极少.我看到用shell的人很多,你建议人用python,人说py是很好,但下一秒 ...

  3. Vue router-link路由不同的写法,不一样的效果

    我想要的路径:http://localhost:8080/#/main/hato/realtime/eventDetail/238 情况一:未进行路由配置:  {      path: 'eventD ...

  4. mysql百万级数据分页查询缓慢优化-实战

    作为后端攻城狮,在接到分页list需求的时候,内心是这样的 画面是这样的 代码大概是这样的 select count(id) from …       查出总数 select * from …. li ...

  5. C++:Special Member Functions

    Special Member Functions 区别于定义类的行为的普通成员函数,类内有一类特殊的成员函数,它们负责类的构造.拷贝.移动.销毁. 构造函数 构造函数控制对象的初始化过程,具体来说,就 ...

  6. Spring @CrossOrigin 通配符 解决跨域问题

    @CrossOrigin 通配符 解决跨域问题 痛点: 对很多api接口需要 开放H5 Ajax跨域请求支持 由于环境多套域名不同,而CrossOrigin 原生只支持* 或者具体域名的跨域支持 所以 ...

  7. 由OSS AccessKey泄露引发的思考

    什么是OSS? 对象存储服务(Object Storage Service,OSS)是一种海量.安全.低成本.高可靠的云存储服务,适合存放任意类型的文件.容量和处理能力弹性扩展,多种存储类型供选择,全 ...

  8. kali渗透综合靶机(十五)--Breach-1.0靶机

    kali渗透综合靶机(十五)--Breach-1.0靶机 靶机下载地址:https://download.vulnhub.com/breach/Breach-1.0.zip 一.主机发现 1.netd ...

  9. Spring-AOP源码分析随手记(二)

    这次来分析下切面的执行过程. 1.怎么看? 怎么开始看源码呢?就直接从被增强的方法调用那里打断点,看看怎么执行的: 然后就来到了这: 2.初步分析 里面有段: if (this.advised.exp ...

  10. .NET WebFrom跨时区项目时间问题处理方法

    前段时间因为公司的一个 WebFrom 项目设计到跨时区的问题,处理了一段时间,终于解决了,写个博客记录一下,方便以后回顾以及给他人提供一个参考的方法. 本次的项目因为跨越了多个时区,在一些时间上会受 ...