LeetCode-126. Word Ladder II [C++][Java]

相关题目: 
LeetCode-127. Word Ladder [C++][Java]_贫道绝缘子的博客-CSDN博客Atransformation sequencefrom wordbeginWordto wordendWordusing a dictionarywordListis a sequence of wordsbeginWord -> s1-> s2-> ... -> sk , return shortest sequence length.https://blog.csdn.net/qq_15711195/article/details/122800843

本题为加强版: 

LeetCode-126. Word Ladder IIhttps://leetcode.com/problems/word-ladder-ii/

题目描述

transformation sequence from word beginWord to word endWord using a dictionary wordList is a sequence of words beginWord -> s1 -> s2 -> ... -> sk such that:

  • Every adjacent pair of words differs by a single letter.
  • Every si for 1 <= i <= k is in wordList. Note that beginWord does not need to be in wordList.
  • sk == endWord

Given two words, beginWord and endWord, and a dictionary wordList, return all the shortest transformation sequences from beginWord to endWord, or an empty list if no such sequence exists. Each sequence should be returned as a list of the words [beginWord, s1, s2, ..., sk].

Example 1:

Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
Output: [["hit","hot","dot","dog","cog"],["hit","hot","lot","log","cog"]]
Explanation: There are 2 shortest transformation sequences:
"hit" -> "hot" -> "dot" -> "dog" -> "cog"
"hit" -> "hot" -> "lot" -> "log" -> "cog"

Example 2:

Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
Output: []
Explanation: The endWord "cog" is not in wordList, therefore there is no valid transformation sequence.

Constraints:

  • 1 <= beginWord.length <= 5
  • endWord.length == beginWord.length
  • 1 <= wordList.length <= 1000
  • wordList[i].length == beginWord.length
  • beginWordendWord, and wordList[i] consist of lowercase English letters.
  • beginWord != endWord
  • All the words in wordList are unique.

解题思路

【C++解法】

1. BFS:set + queue + vector

//这里再用map + vector就不合适了

        unordered_map<string, vector<string>> mp;
        for (string& w:wordList) 
            for(int i=0; i<w.size(); i++) 
                mp[w.substr(0, i) + "#" + w.substr(i+1)].push_back(w);

....

       mp.erase(key);

很明显dog log都和#og相关,erase会导致另一条路不能再走。

class Solution {
public:
    vector<vector<string>> findLadders(string beginWord, string endWord, vector<string>& wordList) {
        vector< vector<string>> res;
        unordered_set<string> dict(wordList.begin(),wordList.end());
        if (dict.find(endWord) == dict.end()) {return res;}
        queue<vector<string>> q; 
        q.push({beginWord});
		bool flag = false; 
        while (!q.empty()){
            int size = q.size();
            while (size--){
                vector<string> curPath = q.front(); q.pop();
                string last = curPath.back();
                if (last == endWord) {
                    res.push_back(curPath);
					flag = true;
					continue;
                }
                dict.erase(last);
                for (int i=0; i<last.size(); i++){
                    string temp = last;
                    for (char ch='a'; ch<='z'; ch++){
                        temp[i] = ch;
                        if (dict.find(temp) != dict.end()){
                            curPath.push_back(temp);
                            q.push(curPath);
                            curPath.pop_back();
                        }
                    }
                }
            }
            if (flag) {break;}
        }
        return res;
    }
};

【Java解法】

1. BFS:Set + Queue + List

class Solution {
    public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
        List<List<String>> res = new ArrayList<>();
        Set<String> dict = new LinkedHashSet<>(wordList);
        if (!dict.contains(endWord)) {return res;}
        Queue<List<String>> q = new LinkedList<>(); 
        q.offer(new ArrayList<>(Arrays.asList(beginWord)));
		boolean flag = false; 
        while (!q.isEmpty()){
            int size = q.size();
            while (size-- > 0){
                List<String> curPath = q.poll();
                String last = curPath.get(curPath.size()-1);
                if (last.equals(endWord)) {
                    res.add(curPath);
					flag = true;
					continue;
                }
                dict.remove(last);
                for (int i=0; i<last.length(); i++){
                    char[] temp = last.toCharArray();
                    for (char ch='a'; ch<='z'; ch++){
                        temp[i] = ch;
                        String newWord = String.valueOf(temp);
                        if (dict.contains(newWord)){
                            curPath.add(newWord);
                            q.offer(new ArrayList<>(curPath));
                            curPath.remove(curPath.size()-1);
                        }
                    }
                }
            }
            if (flag) {break;}
        }
        return res;
    }
}

2. Graph + BFS + DFS(reverse)

This solution search from endWord to beginWord, firstly do bfs to get shortest path and store distance and neighbor words infomation, secondly do dfs to get the paths:
1.Use a HashMap to record the distance between every word in wordlist and the beginWord during bfs.
2.Use a HashMap to record the the neighbor words list of the word(direction: from endWord to beginWord) during bfs.
3.Do dfs to add the words on the path and generate the answer.


class Solution {
    public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
        Set<String> dict = new HashSet<>(wordList);
        List<List<String>> ans = new ArrayList<>();
        if (!dict.contains(endWord)) {return ans;}
        Map<String, List<String>> graph = new HashMap<>();
        Map<String, Integer> dist = new HashMap<>();
        buildGraphByBFS(beginWord, graph, dist, dict);
        dfs(endWord, beginWord, graph, dist, new ArrayList<>(), ans);
        return ans;
    }
    
    public void buildGraphByBFS(String beginWord, Map<String, List<String>> graph,
                    Map<String, Integer> dist, Set<String> dict) {
        Queue<String> queue = new LinkedList<>();
        queue.offer(beginWord);
        dist.put(beginWord, 0);
        for (String word : dict) {graph.put(word, new ArrayList<>());}
        while (!queue.isEmpty()) {
            String cur = queue.poll();
            List<String> neighbors = neighbors(cur, dict);
            for (String next : neighbors) {
                graph.get(next).add(cur);
                if (!dist.containsKey(next)) {
                    dist.put(next, dist.get(cur) + 1);
                    queue.offer(next);
                }
            }
        }
    } 
    
    public void dfs(String curWord, String beginWord, Map<String, List<String>> graph,
                    Map<String, Integer> dist, List<String> path, List<List<String>> ans) {
        path.add(curWord);
        if (curWord.equals(beginWord)) {
            Collections.reverse(path);
            ans.add(new ArrayList<>(path));
            Collections.reverse(path);
        } else {
            for (String next : graph.get(curWord)) {
                if (dist.containsKey(next) && dist.get(curWord) == dist.get(next) + 1) {
                    dfs(next, beginWord, graph, dist, path, ans);
                }
            }
        }
        path.remove(path.size() - 1);
    }
    
    public List<String> neighbors(String word, Set<String> dict) {
        List<String> ans = new ArrayList<>();
        for (int i = 0; i < word.length(); i++) {
            char[] sc = word.toCharArray();
            for (char c = 'a'; c <= 'z'; c++) {
                sc[i] = c;
                String temp = new String(sc);
                if (dict.contains(temp)) {ans.add(temp);}
            }
        }
        return ans;
    }
}

3. Graph + BFS + DFS

do not reverse

class Solution {
    public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) {
        List<List<String>> res = new ArrayList<>(); // to store result
        Map<String, List<String>> graph = new HashMap<>(); // key -> word, value -> List of neighbors of 'word'
        Set<String> dict = new HashSet<>(wordList);
        buildGraph(beginWord, endWord, dict, graph); // BFS step tp form the graph
        shortestPath(beginWord, endWord, graph, res, new ArrayList<>()); // DFS step to find res
        return res;
    }
    
    // build graph - BFS
    // TC : O(N * W*26 + N * (N-1))
    private void buildGraph(String begin, String end, Set<String> dict, Map<String, List<String>> graph) {
        Queue<String> q = new LinkedList<>();
        Set<String> visited = new HashSet<>();
        Set<String> toBeVisited = new HashSet<>();
        boolean foundEnd = false;
        q.add(begin);
        toBeVisited.add(begin);
        while(!q.isEmpty()) {
            visited.addAll(toBeVisited);
            toBeVisited.clear();
            int sz = q.size();
            while (sz-- > 0) { // TC : O(N)
                String currWord = q.poll();
                for(String neighbor : getNeighbor(currWord, dict)) { // TC : O(N - 1)
                    if(end.equals(neighbor)) {foundEnd = true;}
                    if(!visited.contains(neighbor)) { // if neighbor has not been visited yet
                        graph.putIfAbsent(currWord, new ArrayList<>());
                        graph.get(currWord).add(neighbor);
                    }
                    if(!visited.contains(neighbor) && !toBeVisited.contains(neighbor)) {
                        q.add(neighbor);
                        toBeVisited.add(neighbor);
                    }
                }
            }
            if(foundEnd) {break;}
        }
    }
    
    // find the shortest path to endWord - DFS
    // TC : O(N) -> N = wordList.length
    // SC : O(N) -> recursion stack
    private void shortestPath(String currWord, String endWord,
      Map<String, List<String>> graph, List<List<String>> res, ArrayList<String> temp) {
        temp.add(currWord);
        if(currWord.equals(endWord)) {res.add(new ArrayList<>(temp));}
        else if (graph.containsKey(currWord)) {
            for(String neighbor : graph.get(currWord))
                shortestPath(neighbor, endWord, graph, res, temp);
        }
        temp.remove(temp.size() - 1);
    }
    
    // TC : O(W * 26)
    // SC : O(W) -> W = word.length
    private List<String> getNeighbor(String word, Set<String> dict) {
        List<String> res = new ArrayList<>();
        for(int i = 0; i < word.length(); i++) {
            char[] temp = word.toCharArray();
            for(char ch = 'a'; ch <= 'z'; ch++) {
                temp[i] = ch;
                String newWord = String.valueOf(temp);
                if(dict.contains(newWord)) {res.add(newWord);}
            }
        }
        return res;
    }
}

参考文献

【1】【Java】Java双端队列Deque使用详解_devnn的专栏-CSDN博客_deque java

【2】Java HashSet remove()方法与示例_cumubi7453的博客-CSDN博客

【3】Java List初始化7种方式_旭东怪的博客-CSDN博客

【4】List、Set、Map的区别_weixin_30571465的博客-CSDN博客

【5】Java--集合大全List,Set,Map超详细讲解_Kevin.wang-CSDN博客_list、map、set

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 好的,我来用中文回复这个链接:https://leetcode-cn.com/tag/dynamic-programming/ 这个链接是 LeetCode 上关于动态规划的题目集合。动态规划是一种常用的算法思想,可以用来解决很多实际问题,比如最长公共子序列、背包问题、最短路径等等。在 LeetCode 上,动态规划也是一个非常重要的题型,很多题目都需要用到动态规划的思想来解决。 这个链接里包含了很多关于动态规划的题目,按照难度从简单到困难排列。每个题目都有详细的题目描述、输入输出样例、题目解析和代码实现等内容,非常适合想要学习动态规划算法的人来练习和提高自己的能力。 总之,这个链接是一个非常好的学习动态规划算法的资源,建议大家多多利用。 ### 回答2: 动态规划是一种算法思想,通常用于优化具有重叠子问题和最优子结构性质的问题。由于其成熟的数学理论和强大的实用效果,动态规划在计算机科学、数学、经济学、管理学等领域均有重要应用。 在计算机科学领域,动态规划常用于解决最优化问题,如背包问题、图像处理、语音识别、自然语言处理等。同时,在计算机网络和分布式系统中,动态规划也广泛应用于各种优化算法中,如链路优化、路由算法、网络流量控制等。 对于算法领域的程序员而言,动态规划是一种必要的技能和知识点。在LeetCode这样的程序员平台上,题目分类和标签设置十分细致和方便,方便程序员查找并深入学习不同类型的算法。 LeetCode的动态规划标签下的题目涵盖了各种难度级别和场景的问题。从简单的斐波那契数列、迷宫问题到可以用于实际应用的背包问题、最长公共子序列等,难度不断递进且话题丰富,有助于开发人员掌握动态规划的实际应用技能和抽象思维模式。 因此,深入LeetCode动态规划分类下的题目学习和练习,对于程序员的职业发展和技能提升有着重要的意义。 ### 回答3: 动态规划是一种常见的算法思想,它通过将问题拆分成子问题的方式进行求解。在LeetCode中,动态规划标签涵盖了众多经典和优美的算法问题,例如斐波那契数列、矩阵链乘法、背包问题等。 动态规划的核心思想是“记忆化搜索”,即将中间状态保存下来,避免重复计算。通常情况下,我们会使用一张二维表来记录状态转移过程中的中间值,例如动态规划求解斐波那契数列问题时,就可以定义一个二维数组f[i][j],代表第i项斐波那契数列中,第j个元素的值。 在LeetCode中,动态规划标签下有众多难度不同的问题。例如,经典的“爬楼梯”问题,要求我们计算到n级楼梯的方案数。这个问题的解法非常简单,只需要维护一个长度为n的数组,记录到达每一级楼梯的方案数即可。类似的问题还有“零钱兑换”、“乘积最大子数组”、“通配符匹配”等,它们都采用了类似的动态规划思想,通过拆分问题、保存中间状态来求解问题。 需要注意的是,动态规划算法并不是万能的,它虽然可以处理众多经典问题,但在某些场景下并不适用。例如,某些问题的状态转移过程比较复杂,或者状态转移方程中存在多个参数,这些情况下使用动态规划算法可能会变得比较麻烦。此外,动态规划算法也存在一些常见误区,例如错用贪心思想、未考虑边界情况等。 总之,掌握动态规划算法对于LeetCode的学习和解题都非常重要。除了刷题以外,我们还可以通过阅读经典的动态规划书籍,例如《算法竞赛进阶指南》、《算法与数据结构基础》等,来深入理解这种算法思想。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贫道绝缘子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值