Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LeetCode] 269. Alien Dictionary #269

Open
grandyang opened this issue May 30, 2019 · 9 comments
Open

[LeetCode] 269. Alien Dictionary #269

grandyang opened this issue May 30, 2019 · 9 comments

Comments

@grandyang
Copy link
Owner

grandyang commented May 30, 2019

 

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(256);
        queue<char> q;
        string res;
        for (auto a : words) ch.insert(a.begin(), a.end());
        for (int i = 0; i < (int)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]) {
                    st.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 : st) ++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 : st) {
                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,为了节省空间,不必像上面的解法中一样使用一个 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(26, vector<bool>(26));
        vector<bool> visited(26);
        string res;
        for (string word : words) {
            for (char c : word) {
                g[c - 'a'][c - 'a'] = true;
            }
        }
        for (int i = 0; i < (int)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, 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 = 0; i < 26; ++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 同步地址:

#269

 

类似题目:

Minimum Height Trees

Course Schedule II

Course Schedule

 

参考资料:

https://leetcode.com/problems/alien-dictionary/

https://leetcode.com/problems/alien-dictionary/discuss/70169/my-concise-java-solution-based-on-topological-sorting

 

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

@LukLau
Copy link

LukLau commented Jan 10, 2020

求助或者疑问

针对这类题型。大神有和见解或者学习方式。能否指点下

@zengyijing
Copy link

zengyijing commented Feb 5, 2020

BFS的方法有bug
应该用priority_queue<char, vector< char >, greater< char > > 来输出每个in degree为0的char

@lld2006
Copy link

lld2006 commented Jun 7, 2020

这个题目是topology sort, solution中的in其实就是in-degree。
另外这个题目可以参考953

@lld2006
Copy link

lld2006 commented Jun 7, 2020

BFS的方法有bug
应该用priority_queue<char, vector< char >, greater< char > > 来输出每个in degree为0的char

详细说说? 为什么

@zengyijing
Copy link

BFS的方法有bug
应该用priority_queue<char, vector< char >, greater< char > > 来输出每个in degree为0的char

详细说说? 为什么

抱歉看错了,我做的是另一个平台的版本,要求在如果有多种排序的情况下输出最小序列。

@lei-hsia
Copy link

这怎么想到是bfs的?我一开始看觉得是trie,后来发现不对怎么都做不出来

@wanggujin
Copy link

DFS中
if (!g[idx][idx]) return true;
这一句是什么意思?

@JW9506
Copy link

JW9506 commented Feb 16, 2021

DFS中
if (!g[idx][idx]) return true;
这一句是什么意思?

同问, 下面还有句

if (!g[idx][idx]) return true;
...
g[idx][idx] = false;

不知道怎样做解决了什么问题, 求帮忙.

@harryzhang1005
Copy link

harryzhang1005 commented Nov 21, 2021

Thanks for sharing great solutions! 👍
BFS solution, use HashMap/Dictionary to build directed graph will be better. For example

var graph = [Character: Set<Character>]()  // [start: Set<end>]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
8 participants