前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >米哈游笔试原题,网友直呼太难!

米哈游笔试原题,网友直呼太难!

作者头像
数据结构和算法
发布2024-10-11 13:15:00
发布2024-10-11 13:15:00
10400
代码可运行
举报
运行总次数:0
代码可运行

这道题是力扣的第127题,难度为困难,一网友在米哈游的笔试中遇到这题。实际上这题的代码量还挺多的,如果是在机试上直接敲代码我还能理解,但在笔试中没有任何代码提示的情况下能把它完整的写出来真的是不容易。

还有其他网友直呼太难。。。

还有在其他的面试中也遇到过。

问题描述

来源:LeetCode第127题

难度:困难

这题说的是给定两个字符串beginWord和endWord,每次只能改变字符串中的一个字符,让他成为一个新的字符串,并且这个新字符串必须存在于集合wordList中,问至少需要多少步可以从beginWord变成endWord,如果不能从beginWord变成endWord就返回0。

示例 1:

输入: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"] 输出:5 解释:一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", 返回它的长度 5。

示例 2:

输入: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"] 输出:0 解释:endWord "cog" 不在字典中,所以无法进行转换。

提示:

  • endWord.length == beginWord.length
  • wordList[i].length == beginWord.length
  • beginWord、endWord 和 wordList[i] 由小写英文字母组成
  • beginWord != endWord
  • wordList 中的所有字符串互不相同

BFS解决

题中说了单词是由小写字母组成,所以单词中的每个字符有25种变化(小写字母有26个,但不能变成自己,否则等于没变),我们就按照这个思路改变单词中的每个字符然后构成一个新的单词,如果这个新的单词存在于字典中并且又没有被访问过我们就可以把它当做树的一个节点,添加到队列中,我们以示例1为例来画个图看下,这棵树我把它横着放了。

来看下代码:

代码语言:javascript
代码运行次数:0
复制
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
    // 把字典中的单词放入到set中,主要是为了方便查询
    Set<String> dictSet = new HashSet<>(wordList);
    Set<String> visited = new HashSet<>();
    Queue<String> queue = new LinkedList<>();
    queue.add(beginWord);// 把起始点添加到队列中
    int level = 0;
    while (!queue.isEmpty()) {
        int levelCount = queue.size();// 当前层的节点个数
        level++;// 第几层
        // 遍历当前层的所有字符串
        while (levelCount-- > 0) {
            String word = queue.poll();
            // 下面是修改字符串的字符,然后生成一个新的字符串
            for (int j = 0; j < word.length(); j++) {
                char[] ch = word.toCharArray();
                for (char c = 'a'; c <= 'z'; c++) {
                    if (c == word.charAt(j))
                        continue;
                    ch[j] = c;
                    // 修改其中的一个字符,然后重新构建一个新的单词
                    String newWord = String.valueOf(ch);
                    // 如果字典中包含newWord,并且newWord没有被访问过,就把他加入到队列中
                    if (dictSet.contains(newWord) && visited.add(newWord)) {
                        // 加入队列之前判断newWord是不是我们要找的,如果是就直接返回
                        if (newWord.equals(endWord))
                            return level + 1;
                        queue.add(newWord);
                    }
                }
            }
        }
    }
    return 0;
}

双向BFS解决

上面的只是通过一个方向查找,就是从起点到终点,如果字典中字符串比较多的话,这棵树是非常胖的,如下图所示。

如果要查找到endWord字符串我们需要遍历图中阴影部分的所有节点,可以看到这个节点非常多。我们来逆向思维一下,如果endWord字符串不在字典中我们肯定是查找不到的,直接返回0即可。如果endWord字符串在字典中我们可以用endWord作为根节点逆向查找,如下所示。

可以看到上面图中两棵树的公共部分明显比一棵树的小很多。图中的这两棵树我们都可以遍历,每次都只能遍历其中的一棵并且每次只能遍历一层,哪层节点少我们就先遍历哪层,当在某一层他们有公共字符串的时候,只需要返回这两棵树的高度和即可,我们画个图看下

对于两棵树的高度和我们可以使用一个变量levelTotal来记录,无论那棵树,只要遍历一层,levelTotal就加1。至于某一层他们是否有公共字符串,我们可以一边遍历一边查询,比如遍历第一棵树的时候我们需要在第二棵树中查询,遍历第二棵树的时候需要在第一棵树中查询,来看下代码:

代码语言:javascript
代码运行次数:0
复制
public int ladderLength(String beginWord, String endWord, List<String> wordList) {
    // 这里要注意,如果endWord字符串不在字典中,无论怎么转换都不行的
    if (!wordList.contains(endWord))
        return 0;
    Set<String> dictSet = new HashSet<>(wordList);
    Set<String> visit = new HashSet<>();
    // 创建两个队列
    Queue<String> startQueue = new LinkedList<>();
    Queue<String> endQueue = new LinkedList<>();
    startQueue.add(beginWord);
    endQueue.add(endWord);
    int levelTotal = 1;
    while (!startQueue.isEmpty() && !endQueue.isEmpty()) {
        boolean res;
        // 从少的开始遍历
        if (startQueue.size() <= endQueue.size()) {
            // 先遍历start树,然后在end树中查询
            res = bfs(startQueue, endQueue, dictSet, visit);
        } else {
            // 先遍历end树,然后在start树中查询
            res = bfs(endQueue, startQueue, dictSet, visit);
        }
        levelTotal++;// 遍历一层levelTotal就加1
        // 如果返回true,说明在某一层有公共的字符串,直接返回总的访问层数levelTotal
        if (res) return levelTotal;
    }
    return 0;
}

// 每次只能遍历一层,第一个队列是需要遍历的,第二个队列是待查询的
public boolean bfs(Queue<String> queue, Queue<String> query,
                   Set<String> dictSet, Set<String> visited) {
    int levelCount = queue.size();// 当前队列中元素的个数
    while (levelCount-- > 0) {
        String word = queue.poll();
        // 下面是修改字符串的字符,然后生成一个新的字符串
        for (int j = 0; j < word.length(); j++) {
            char[] ch = word.toCharArray();
            for (char c = 'a'; c <= 'z'; c++) {
                if (c == word.charAt(j))
                    continue;
                ch[j] = c;
                // 修改其中的一个字符,然后重新构建一个新的单词
                String newWord = String.valueOf(ch);
                // 新的字符串是否存在字典中
                if (dictSet.contains(newWord)) {
                    // queue可以看做当前树的某一层的集合,
                    // query是另一棵树某一层的集合。
                    if (query.contains(newWord))
                        return true;
                    if (visited.add(newWord))
                        queue.offer(newWord);
                }
            }
        }
    }
    return false;
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-11-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据结构和算法 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档