剑指 Offer II 114. 外星文字典(269. 火星词典)

题目:

思路:

【1】拓扑排序 + 深度优先搜索

【2】拓扑排序 + 广度优先搜索

代码展示:

拓扑排序 + 广度优先搜索

//时间2 ms击败99.58%
//内存40 MB击败44.88%
思路:
1、先标记所有出现过的字母,没出现过的字母不要在答案中出现;
2、正向建图(存储某个字母的儿子):用list存储,反向建图,用set存储;
3、把所有出现过的点(字母)并且没有父节点的(入度为0)点先拿出来组成字母表前缀,这些是字母表中最可能的最靠前的字母们;
4、BFS取出每一个字母,同时把它从相应儿子的set中删除,假如此时set已经空,这说明这个点的祖先你们全都遍历过了,而且也加到字母表里面了,那么这个字母也能够假如字母表;
5、此时遍历所有set,假如还有不空的,说明拓扑排序存在环,则不存再答案;
6、对于有些出现了的,但是还没有跟其他字母存在先后关系的字母,也要拼在字母表后边,既然出现了,也不可以落下。
注意陷阱: 
1、建图不要重复加入边,出错案例:["ac","ab","zc","zb"] 
2、后者不能是前者的真前缀,出错案例:["abc","ab"]

class Solution {
    public String alienOrder(String[] words) {
        boolean has[]=new boolean[26];//记录字母是否出现过
        char c[][]=new char[words.length][];
        for(int i=0;i<c.length;i++){
            c[i]=words[i].toCharArray();
            for(int j=0;j<c[i].length;j++){has[c[i][j]-'a']=true;}
        }
        List<Integer> son[]=new List[26];
        Set<Integer> father[]=new Set[26];
        for(int i=0;i<26;i++){
            son[i]=new ArrayList<>();
            father[i]=new HashSet<>();
        }
        //以下正反建图:
        for(int i=1;i<c.length;i++){
            int p=0;
            while(p<Math.min(c[i-1].length,c[i].length)&&c[i-1][p]==c[i][p]){p++;}
            if(p==c[i].length&&p<c[i-1].length){return "";}//后者为前者的真前缀,报警
            if(p<c[i].length&&p<c[i-1].length){
                //此时遇到的首个不同字母,有关字母表顺序,主注意去重
                if(father[c[i][p]-'a'].add(c[i-1][p]-'a')){son[c[i-1][p]-'a'].add(c[i][p]-'a');}
            }
        }
        Queue<Integer> q=new LinkedList<>();
        StringBuilder ans=new StringBuilder();
        //先把没有父节点的字母收集起来,这些是字母中的前排
        for(int i=0;i<26;i++){
            if(has[i]&&father[i].size()==0){
                ans.append((char)(i+'a'));
                has[i]=false;
                q.add(i);
            }
        }
        //下边开始BFS:
        while(q.size()>0){
            int a=q.poll();
            for(int i=0;i<son[a].size();i++){
                int b=son[a].get(i);
                father[b].remove(a);
                if(father[b].size()==0){
                    ans.append((char)(b+'a'));
                    has[b]=false;
                    q.add(b);
                }
            }
        }
        //下边开始检查有无set不是空的,有的话说明存在环,顺便把无顺序关系的字母加进来
        for(int i=0;i<26;i++){
            if(father[i].size()>0){return "";}
            if(has[i]){ans.append((char)(i+'a'));}
        }
        return ans.toString();
    }
}

 

拓扑排序 + 深度优先搜索:

//时间2 ms击败99.58%
//内存39.5 MB击败84.55%
class Solution {
    List<List<Character>> adj = new ArrayList<>();
    StringBuilder builder = new StringBuilder();
    boolean[] visited = new boolean[26];
    boolean[] onPath = new boolean[26];
    boolean[] occur = new boolean[26];
    boolean hasCycle = false;
    boolean error = false;

    public String alienOrder(String[] words) {
        for (int i = 0; i < 26; i++) {
            adj.add(new ArrayList<>());
        }
        for (String word : words) {
            for (int i = 0; i < word.length(); i++) {
                occur[word.charAt(i) - 'a'] = true;
            }
        }
        for (int i = 0; i < words.length - 1; i++) {
            buildAdj(words[i], words[i + 1]);
            if(error){
                return "";
            }
        }
        for (char c = 'a'; c <= 'z'; c++) {
            if (occur[c - 'a']) {
                dfs(c);
                if (hasCycle) {
                    return "";
                }
            }
        }
        return builder.reverse().toString();
    }

    public void buildAdj(String s, String t) {
        int index = 0;
        int minLen = Math.min(s.length(), t.length());
        while (index < minLen && s.charAt(index) == t.charAt(index)) {
            index++;
        }
        if (index < minLen) {
            adj.get(s.charAt(index) - 'a').add(t.charAt(index));
        }else if(s.length() != minLen){
            error = true;
        }
    }

    public void dfs(char c) {
        int index = c - 'a';
        if (onPath[index]) {
            hasCycle = true;
        }
        if (visited[index] || hasCycle) {
            return;
        }
        onPath[index] = true;
        visited[index] = true;
        for (Character d : adj.get(index)) {
            dfs(d);
        }
        onPath[index] = false;
        builder.append(c);
    }
}

 

posted @ 2023-05-04 11:14  忧愁的chafry  阅读(29)  评论(0编辑  收藏  举报