[LeetCode] 269. Alien Dictionary 解题报告

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:

  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.


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

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

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

        }
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值