[算法]LeetCode每日一题--126单词接龙 II(Java)

DailyChallenge

126. 单词接龙 II

Hard20200607

Description

给定两个单词(beginWord 和 endWord)和一个字典 wordList,找出所有从 beginWord 到 endWord 的最短转换序列。转换需遵循如下规则:

每次转换只能改变一个字母。
转换过程中的中间单词必须是字典中的单词。

说明:

  • 如果不存在这样的转换序列,返回一个空列表。
  • 所有单词具有相同的长度。
  • 所有单词只由小写字母组成。
  • 字典中不存在重复的单词。
  • 你可以假设 beginWord 和 endWord 是非空的,且二者不相同。

示例 1:

输入:
beginWord = "hit",
endWord = "cog",
wordList = ["hot","dot","dog","lot","log","cog"]

输出:
[
  ["hit","hot","dot","dog","cog"],
  ["hit","hot","lot","log","cog"]
]

示例 2:

输入:
beginWord = "hit"
endWord = "cog"
wordList = ["hot","dot","dog","lot","log"]

输出: []

解释: endWord "cog" 不在字典中,所以不存在符合要求的转换序列。

Solution

  1. 广度优先搜索(BFS): while + queue
  2. 队列先进先出,尾部进,头部出
  3. 题解都在注释中
class Solution {
    // 单词建图,广度优先搜索,BFS
    // 单词到id的映射
    // 看官方题解的,感觉如果有一个pair的数据结构不用这么麻烦
    private Map<String, Integer> word2Id;
    // id到单词的映射,下标对应id,直接用数组就可以了
    private ArrayList<String> id2Word;
    // 存图的边
    private ArrayList<Integer>[] edges;
    private static final int INF = 1 << 15;

    public Solution(){
        word2Id = new HashMap<>();
        id2Word = new ArrayList<>();
    }

    public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
        // 建图
        // 先设置顶点
        int id = 0;
        for(; id < wordList.size(); id++){
            word2Id.put(wordList.get(id), id);
            id2Word.add(wordList.get(id));
        }
        if(!word2Id.containsKey(beginWord)){
            word2Id.put(beginWord, id);
            id2Word.add(beginWord);
        }
        // 如果map没有endWord就没有解
        if(!word2Id.containsKey(endWord)){
            return new ArrayList<>();
        }

        // 处理边了
        // 每个顶点要连接改变一个字母可达的位置,所以每个edge也是一个数组
        // ArrayList(n),新建一个长度为n的ArrayList类型数组
        edges = new ArrayList[id2Word.size()];
        // 每一个edge[i]应该是一个数组,因为有不同数量相连的边
        // 初始化一下
        for(int i = 0; i < id2Word.size(); i++){
            edges[i] = new ArrayList<>();
        }
        // 判断是否可达,初始化边
        for(int i = 0; i < id2Word.size(); i++){
            // 无向图,所以j = i + 1;
            for(int j = i + 1; j < id2Word.size(); j++){
                if(isArrive(id2Word.get(i), id2Word.get(j))){
                    edges[i].add(j);
                    edges[j].add(i);
                }
            }
        }

        int endId = word2Id.get(endWord);

        // 答案
        List<List<String>> res = new ArrayList<>();

        // 求最短转换序列,所以添加cost变量,记录转化次数
        // 到达每个点的cost
        int[] cost = new int[id2Word.size()];
        // 求最小,一般代价都要设置为最大值
        for(int i = 0; i < id2Word.size(); i++){
            cost[i] = INF;
        }

        // 广度优先搜索,就是queue+while的组合
        // 先把起点加进去
        Queue<ArrayList<Integer>> q = new LinkedList<>();
        ArrayList<Integer> tmpBegin = new ArrayList<>();
        tmpBegin.add(word2Id.get(beginWord));
        q.add(tmpBegin);
        cost[word2Id.get(beginWord)] = 0;
        while(!q.isEmpty()){
            // curr存的是id
            ArrayList<Integer> curr = q.poll();
            int last = curr.get(curr.size() - 1);
            // 若为终点,则res加一个路径
            if(last == endId){
                ArrayList<String> ans = new ArrayList<>();
                for(int c : curr){
                    // 由curr的路径id获得string,返回结果
                    ans.add(id2Word.get(c));
                }
                res.add(ans);
            }else{
                // 不为终点就继续找
                for(int i = 0; i < edges[last].size(); i++){
                    int toId = edges[last].get(i);

                    // =是为了记录相同路径;< 因为cost初始化最大值;> 就说明之前来过,有更小的值
                    if(cost[last] + 1 <= cost[toId]){
                        cost[toId] = cost[last] + 1;
                        ArrayList<Integer> tmp = new ArrayList<Integer>(curr);
                        tmp.add(toId);
                        q.add(tmp);
                    }
                }
            }

        }
        return res;
    }
    public boolean isArrive(String a, String b){
        int diff = 0;
        for(int i = 0; i < a.length(); i++){
            if(a.charAt(i) != b.charAt(i)){
                diff++;
                // if(diff > 1);
                // return false;
            }
        }
        return diff == 1;
    }
}

欢迎关注微信公众号GitKid,每天分享LeetCode题解,让你走着看算法。

GitKid

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值