您的位置:首页 > 运维架构

[LeetCode] 269. Alien Dictionary 解题报告

2017-03-05 02:30 363 查看
There is a new alien language which uses the latin alphabet. However, the order among letters are unknown to you. You receive a list ofnon-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:

Given the following words in dictionary,
[
"wrt",
"wrf",
"er",
"ett",
"rftt"
]


The correct order is: 
"wertf"
.

Example 2:

Given the following words in dictionary,
[
"z",
"x"
]


The correct order is: 
"zx"
.

Example 3:

Given the following words in dictionary,
[
"z",
"x",
"z"
]


The order is invalid, so return 
""
.

Note:

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

这一题是付费题,难度为hard,我个人觉得如果能看出来套路,就不难了。
首先,能想到拓扑排序(不要问我怎么想到的),这一题基本也就解决了一大半了。详细来说
,我们需要在脑海中构建一个图,图的节点就是字母,然后图的边是什么呢???这是一个很关键的地方,我一开始就搞错了 ,我感觉也是题目故意不交代清楚。
我一开始以为单词内的 字母要按顺序排,因此图的边就是一个单词内的字母之间的顺序,后来发现example1,就不符合这个规律。(真的是写完才发现的,特别麻烦。)

实际上,边是按照相邻单词之间不同字母的顺序来生成的,比如“abc”,“abd”,两个单词相邻,依次比较单词的各个字母,找到第一个不同的,也就是cd,那么c->d就形成一个边。以此类推比较完所有相邻的单词即可。(在本题中,遍历两个字符串中较小的长度,如果没有不同的字符,则忽略)

具体方法:

生成三个数组,1.int[26]存储不同节点的入度(实际上这里应该使用最小堆,但是由于只有26个字母,用数组效率更高),2.一个存储边对边数量int[26][26](实际上这里就是存储图的,用的是邻接矩阵方式存储图,而不是邻接链表,因为只有26个字母,所以很有限),3.int[26]第三个数组用来存储字母的出现顺序,用来应对在出现多种拓扑排序的结果时,用来确定哪个字母放在前面的;
初始化数组1,所有的值为-1,将出现了的字符置为0;并记录字符出现的顺序到数组3;获取相邻字符串的第一个不同字符,作为边,记录到数组2,当出现一个边时,还要对入度数组1对应的节点的数量++;(这一步实际上就是拓扑排序的准备工作,图结构和入度最小堆的构造)
拓扑排序。(将入度为0的节点取出以后,要将数组1中当前位置的值置为-1。假如出现多个入度为0的节点,则使用数组3进行排序)
检查入度数组,是否所有的值都为-1,如果不是则说明图中有环,输出为“”,否则输出结果。(这一步实际上是拓扑排序的收尾工作)

这里简单介绍一下拓扑排序的基本原理:选取一个入度为0的节点(入度数组1),将它加入结果队列list,并在邻接矩阵中去掉以该点为出度的所有边,并且在入度数组1中对应位置--。再选取入度为0的节点,直到所有节点都入度为0,输出list即可。(假如最后发现存在节点入度不为0的节点,说明图中有环,返回空。)
图示:



public class Solution {
private final static int NUM = 26;

int[] nArrInDegreeNum;
int[][] nArrEdges;
int[] nArrOrders;

public String alienOrder(String[] words) {
nArrInDegreeNum = new int[NUM];// store number of indegree, like
// min heap
// init all to -1, which means there is no such element
for (int i = 0; i < NUM; i++) {
nArrInDegreeNum[i] = -1;
}
nArrEdges = new int[26][26];// store edge
nArrOrders = new int[26];// for the order of alphabet
int nOrder = 1;

// init all character appeared to 0, and decide the order of alph
for (String string : words) {
// if it appears and =-2, set it = -1
for (char c : string.toCharArray()) {
if (nArrInDegreeNum[c - 'a'] == -1) {
nArrInDegreeNum[c - 'a'] = 0;
}
if (nArrOrders[c - 'a'] == 0) {
nArrOrders[c - 'a'] = nOrder;
nOrder++;
}
}
}

// compare the neighbor strings, and find the first different char
for (int index = 0; index < words.length; index++) {
if (index < words.length - 1) {
findFirstDifferentChar(words[index], words[index + 1]);
}
}

// top sort
StringBuilder sb = new StringBuilder();
topologicalSorting(sb);

// judge whether all degree is -1, if not return ""
for (int i = 0; i < NUM; i++) {
if (nArrInDegreeNum[i] != -1) {
return "";
}
}
return sb.toString();
}

private void topologicalSorting(StringBuilder sb) {
boolean bFind = true;
LinkedList<Integer> llFind = new LinkedList<>();

while (bFind) {
bFind = false;
for (int i = 0; i < NUM; i++) {
if (nArrInDegreeNum[i] == 0) {
bFind = true;
llFind.add(i);
nArrInDegreeNum[i] = -1;
}
}
// if find the indegree==0
if (bFind) {
// for two same chars, sort as its appeared order
Collections.sort(llFind, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return nArrOrders[o1] - nArrOrders[o2];
}
});
for (Integer integer : llFind) {
char c = (char) ('a' + integer);
sb.append(c);
for (int i = 0; i < NUM; i++) {
if (nArrEdges[integer][i] > 0) {
nArrInDegreeNum[i] -= nArrEdges[integer][i];
nArrEdges[integer][i] = 0;
}
}
}
llFind.clear();
}
}
}

private void findFirstDifferentChar(String str1, String str2) {
char[] c1 = str1.toCharArray();
char[] c2 = str2.toCharArray();
int nLength = Math.min(c1.length, c2.length);
for (int i = 0; i < nLength; i++) {
if (c1[i] != c2[i]) {
nArrInDegreeNum[c2[i] - 'a']++;// indegree num ++
nArrEdges[c1[i] - 'a'][c2[i] - 'a']++;// edge ++
break;
}

}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息