127. Word Ladder

2023-05-16

Given two words (beginWord and endWord), and a dictionary's word list, find the length of shortest transformation sequence from beginWord to endWord, such that:

  1. Only one letter can be changed at a time.
  2. Each transformed word must exist in the word list. Note that beginWord is not a transformed word.

Note:

  • Return 0 if there is no such transformation sequence.
  • All words have the same length.
  • All words contain only lowercase alphabetic characters.
  • You may assume no duplicates in the word list.
  • You may assume beginWord and endWord are non-empty and are not the same.

Example 1:


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

Output: 5

Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
return its length 5.
  

Example 2:


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

Output: 0

Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.
  

Accepted

362,132

Submissions

1,312,806

 

Solution

We are given a beginWord and an endWord. Let these two represent start node and end node of a graph. We have to reach from the start node to the end node using some intermediate nodes/words. The intermediate nodes are determined by the wordList given to us. The only condition for every step we take on this ladder of words is the current word should change by just one letter.

We will essentially be working with an undirected and unweighted graph with words as nodes and edges between words which differ by just one letter. The problem boils down to finding the shortest path from a start node to a destination node, if there exists one. Hence it can be solved using Breadth First Search approach.

One of the most important step here is to figure out how to find adjacent nodes i.e. words which differ by one letter. To efficiently find the neighboring nodes for any given word we do some pre-processing on the words of the given wordList. The pre-processing involves replacing the letter of a word by a non-alphabet say, *.

This pre-processing helps to form generic states to represent a single letter change.

For e.g. Dog ----> D*g <---- Dig

Both Dog and Dig map to the same intermediate or generic state D*g.

The preprocessing step helps us find out the generic one letter away nodes for any word of the word list and hence making it easier and quicker to get the adjacent nodes. Otherwise, for every word we will have to iterate over the entire word list and find words that differ by one letter. That would take a lot of time. This preprocessing step essentially builds the adjacency list first before beginning the breadth first search algorithm.

For eg. While doing BFS if we have to find the adjacent nodes for Dug we can first find all the generic states for Dug.

  1. Dug => *ug
  2. Dug => D*g
  3. Dug => Du*

The second transformation D*g could then be mapped to Dog or Dig, since all of them share the same generic state. Having a common generic transformation means two words are connected and differ by one letter.

Approach 1: Breadth First Search

Intuition

Start from beginWord and search the endWord using BFS.

Algorithm

  1. Do the pre-processing on the given wordList and find all the possible generic/intermediate states. Save these intermediate states in a dictionary with key as the intermediate word and value as the list of words which have the same intermediate word.

  2. Push a tuple containing the beginWord and 1 in a queue. The 1 represents the level number of a node. We have to return the level of the endNode as that would represent the shortest sequence/distance from the beginWord.

  3. To prevent cycles, use a visited dictionary.

  4. While the queue has elements, get the front element of the queue. Let's call this word as current_word.

  5. Find all the generic transformations of the current_word and find out if any of these transformations is also a transformation of other words in the word list. This is achieved by checking the all_combo_dict.

  6. The list of words we get from all_combo_dict are all the words which have a common intermediate state with the current_word. These new set of words will be the adjacent nodes/words to current_word and hence added to the queue.

  7. Hence, for each word in this list of intermediate words, append (word, level + 1) into the queue where level is the level for the current_word.

  8. Eventually if you reach the desired word, its level would represent the shortest transformation sequence length.

    Termination condition for standard BFS is finding the end word.

class Solution {
  public int ladderLength(String beginWord, String endWord, List<String> wordList) {

    // Since all words are of same length.
    int L = beginWord.length();

    // Dictionary to hold combination of words that can be formed,
    // from any given word. By changing one letter at a time.
    Map<String, List<String>> allComboDict = new HashMap<>();

    wordList.forEach(
        word -> {
          for (int i = 0; i < L; i++) {
            // Key is the generic word
            // Value is a list of words which have the same intermediate generic word.
            String newWord = word.substring(0, i) + '*' + word.substring(i + 1, L);
            List<String> transformations = allComboDict.getOrDefault(newWord, new ArrayList<>());
            transformations.add(word);
            allComboDict.put(newWord, transformations);
          }
        });

    // Queue for BFS
    Queue<Pair<String, Integer>> Q = new LinkedList<>();
    Q.add(new Pair(beginWord, 1));

    // Visited to make sure we don't repeat processing same word.
    Map<String, Boolean> visited = new HashMap<>();
    visited.put(beginWord, true);

    while (!Q.isEmpty()) {
      Pair<String, Integer> node = Q.remove();
      String word = node.getKey();
      int level = node.getValue();
      for (int i = 0; i < L; i++) {

        // Intermediate words for current word
        String newWord = word.substring(0, i) + '*' + word.substring(i + 1, L);

        // Next states are all the words which share the same intermediate state.
        for (String adjacentWord : allComboDict.getOrDefault(newWord, new ArrayList<>())) {
          // If at any point if we find what we are looking for
          // i.e. the end word - we can return with the answer.
          if (adjacentWord.equals(endWord)) {
            return level + 1;
          }
          // Otherwise, add it to the BFS Queue. Also mark it visited
          if (!visited.containsKey(adjacentWord)) {
            visited.put(adjacentWord, true);
            Q.add(new Pair(adjacentWord, level + 1));
          }
        }
      }
    }

    return 0;
  }
}

Complexity Analysis

  • Time Complexity: O(M×N), where M is the length of words and N is the total number of words in the input word list. Finding out all the transformations takes M iterations for each of the N words. Also, breadth first search in the worst case might go to each of the N words.

  • Space Complexity: O(M×N), to store all M transformations for each of the N words, in the all_combo_dict dictionary. Visited dictionary is of N size. Queue for BFS in worst case would need space for all N words.

     


Approach 2: Bidirectional Breadth First Search

Intuition

The graph formed from the nodes in the dictionary might be too big. The search space considered by the breadth first search algorithm depends upon the branching factor of the nodes at each level. If the branching factor remains the same for all the nodes, the search space increases exponentially along with the number of levels. Consider a simple example of a binary tree. With each passing level in a complete binary tree, the number of nodes increase in powers of 2.

We can considerably cut down the search space of the standard breadth first search algorithm if we launch two simultaneous BFS. One from the beginWord and one from the endWord. We progress one node at a time from both sides and at any point in time if we find a common node in both the searches, we stop the search. This is known as bidirectional BFS and it considerably cuts down on the search space and hence reduces the time and space complexity.

Algorithm

  1. The algorithm is very similar to the standard BFS based approach we saw earlier.

  2. The only difference is we now do BFS starting two nodes instead of one. This also changes the termination condition of our search.

  3. We now have two visited dictionaries to keep track of nodes visited from the search starting at the respective ends.

  4. If we ever find a node/word which is in the visited dictionary of the parallel search we terminate our search, since we have found the meet point of this bidirectional search. It's more like meeting in the middle instead of going all the way through.

    Termination condition for bidirectional search is finding a word which is already been seen by the parallel search.

  5. The shortest transformation sequence is the sum of levels of the meet point node from both the ends. Thus, for every visited node we save its level as value in the visited dictionary.

class Solution {

  private int L;
  private Map<String, List<String>> allComboDict;

  Solution() {
    this.L = 0;

    // Dictionary to hold combination of words that can be formed,
    // from any given word. By changing one letter at a time.
    this.allComboDict = new HashMap<>();
  }

  private int visitWordNode(
      Queue<Pair<String, Integer>> Q,
      Map<String, Integer> visited,
      Map<String, Integer> othersVisited) {

    Pair<String, Integer> node = Q.remove();
    String word = node.getKey();
    int level = node.getValue();

    for (int i = 0; i < this.L; i++) {

      // Intermediate words for current word
      String newWord = word.substring(0, i) + '*' + word.substring(i + 1, L);

      // Next states are all the words which share the same intermediate state.
      for (String adjacentWord : this.allComboDict.getOrDefault(newWord, new ArrayList<>())) {
        // If at any point if we find what we are looking for
        // i.e. the end word - we can return with the answer.
        if (othersVisited.containsKey(adjacentWord)) {
          return level + othersVisited.get(adjacentWord);
        }

        if (!visited.containsKey(adjacentWord)) {

          // Save the level as the value of the dictionary, to save number of hops.
          visited.put(adjacentWord, level + 1);
          Q.add(new Pair(adjacentWord, level + 1));
        }
      }
    }
    return -1;
  }

  public int ladderLength(String beginWord, String endWord, List<String> wordList) {

    if (!wordList.contains(endWord)) {
      return 0;
    }

    // Since all words are of same length.
    this.L = beginWord.length();

    wordList.forEach(
        word -> {
          for (int i = 0; i < L; i++) {
            // Key is the generic word
            // Value is a list of words which have the same intermediate generic word.
            String newWord = word.substring(0, i) + '*' + word.substring(i + 1, L);
            List<String> transformations =
                this.allComboDict.getOrDefault(newWord, new ArrayList<>());
            transformations.add(word);
            this.allComboDict.put(newWord, transformations);
          }
        });

    // Queues for birdirectional BFS
    // BFS starting from beginWord
    Queue<Pair<String, Integer>> Q_begin = new LinkedList<>();
    // BFS starting from endWord
    Queue<Pair<String, Integer>> Q_end = new LinkedList<>();
    Q_begin.add(new Pair(beginWord, 1));
    Q_end.add(new Pair(endWord, 1));

    // Visited to make sure we don't repeat processing same word.
    Map<String, Integer> visitedBegin = new HashMap<>();
    Map<String, Integer> visitedEnd = new HashMap<>();
    visitedBegin.put(beginWord, 1);
    visitedEnd.put(endWord, 1);

    while (!Q_begin.isEmpty() && !Q_end.isEmpty()) {

      // One hop from begin word
      int ans = visitWordNode(Q_begin, visitedBegin, visitedEnd);
      if (ans > -1) {
        return ans;
      }

      // One hop from end word
      ans = visitWordNode(Q_end, visitedEnd, visitedBegin);
      if (ans > -1) {
        return ans;
      }
    }

    return 0;
  }
}

Complexity Analysis

  • Time Complexity: O(M×N), where M is the length of words and N is the total number of words in the input word list. Similar to one directional, bidirectional also takes M∗N for finding out all the transformations. But the search time reduces to half, since the two parallel searches meet somewhere in the middle.

  • Space Complexity: O(M×N), to store all M transformations for each of the N words, in the all_combo_dict dictionary, same as one directional. But bidirectional reduces the search space. It narrows down because of meeting in the middle.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

127. Word Ladder 的相关文章

  • visio导出高质量图片

    具体操作如下 首先 ctrl A 然后选择 另存为 保存类型选择 Tag图像文件格式 接着在输出里面设置 压缩格式选为 LZW 接着是 256色 然后选择 打印机 下面是 源 然后点击确定就可以了 这样绝对是满足投稿要求的 分辨率为300d
  • word/wps分页

    方法1 将需要设置为奇数页的地方 设置为奇数页 方法2 在需要设置为奇数页的后面插入空白页 或偶数页 但不推荐 可以捣鼓捣鼓
  • 利用docx4j word转pdf

    依赖
  • Linux服务器(centos7)中Word转换PDF,文档出现中文乱码或方格【亲测可用,已解决】

    提示 在centos服务器使用aspose word转换word文件为pdf的时候只有中文乱码或则方格 但是在win服务器上使用可以正常转换 本次文章主要解决字体缺失问题 目录 前言 一 在linux服务器上生成的pdf都是这种格式的 二
  • mac如何利用 applescript 批量将word转换成pdf

    没有更多的废话 直接上代码 代码的注释应该很清楚了 property word docs org openxmlformats wordprocessingml document com microsoft word doc propert
  • Python 动态生成系统数据库设计到word文档

    背景 经常需要交付一些系统文档而且基本都是word的 其中又有系统数据库介绍模块 看着数据库里的几百张表于是我开始怀疑人生 所以咱手写一个 涉及知识 pymysql 操作数据库 tkinter GUI图形库 threading 线程 que
  • 使用Microsoft Word2016无法正常对Latex文本转换的踩坑和解决方法

    相信很多人都遇到像我一样的问题 word2016中 有latex的按钮 按ALT 就可以开始写公式 复制粘贴latex公式之后 怎么就转换不了呢 就是如图这样的 左上角转换按钮为灰色 上网找呀找 找了很多资料 大多数都是介绍用法的 但是转换
  • 【Word】学习笔记|批量解决文档中公式编号不居中的问题

    1 问题描述 当你将一个Word中内容 包含公式 复制到另外一个Ward里 发现MathType公式编号未居中 如上图所示 如果你公式较少 可以参考官方教程解决 就是将段落 中文版式 文本对齐方式中设置为居中就行 官方解决方法 MathTy
  • PPT添加页码

    点击 插入 点击 幻灯片编号 点击 幻灯片编号 点击 全部应用 右下角出现编号
  • 常见文件预览实现

    一 word文档预览 1 使用文档预览服务预览 使用微软链接 https view officeapps live com op view aspx src 文档http地址 使用XDOC链接 http view xdocin com xd
  • 怎样把pdf转换成word-多语言ocr支持

    http jingyan baidu com article 86fae34699bb4e3c49121a23 html PDF格式良好的视觉阅读性和通用性使得PDF文件的使用越来越广泛了 网络上的PDF资料也越来越多 但是我们往往想要提出
  • 教程:Word中如何让参考文献编号和引用标记都是数字上标

    教程 Word中如何让参考文献编号和引用标记都是上标 更新历史 20190509 首次发布 使用Microsoft Word写论文之类的文档的时候 经常需要列出参考文献 并对它们进行引用 有时候 格式规范要求我们将参考文献编号和引用标记都变
  • Office 之将 PPT 图片完美插入 Word

    将 PPT 图片完美插入 Word 原始文档 https www yuque com lart tools wdg4ww 前言 PPT 提供了简单易用的基本绘图支持 而 Word 则提供了专业的文档撰写和处理的支持 但这些工具并不是独立且互
  • 解决word中公式与右编号上下不居中的问题

    1 如图所示 选中右编号敲击公式后 公式与右编号上下不居中 2 在开始 样式 中找到如下图所示的样式 右击 修改 3 进入段落 修改如下图示 然后点击确定 在应用修改后的样式 就好了
  • Java准确获取Word/Excel/PPT/PDF的页数(附Word页数读不准的处理办法)

    Java准确获取Word Excel PPT PDF的页数 附Word页数读不准的处理办法 1 需求背景 2 环境准备工作 2 1 JACOB介绍及安装 2 2 Microsoft Office Word的设置 3 代码 3 1 代码示例
  • Java Word转PDF

    两种方式 documents4j groupdocs 一 documents4j 1 添加依赖
  • POI实现Word文件转PDF

    需求 采用spire doc生成word文件后 需要加一个预览PDF的功能 可以直接采用POI对docx文件进行转换处理 public static void main String args throws Exception String
  • Word标尺怎么调出来?4个方法教会你!

    在使用Word编辑文档时 我需要把标尺调出来 但是不知道应该如何操作 大家有没有简单的方法可以调出Word标尺的呀 在使用Word办公时 Word标尺是一个非常有用的工具 可以帮助用户更好地格式化文档 调整页边距 以及进行段落缩进等操作 但
  • Word——状态栏不显示选择区域中的字数的解决办法

    一 步骤 点击左下角那个 字数 XXX 弹出的框中选中选项 包含文本框 如果已经选中了可以先点掉再选中 关闭所有的word文档 再打开 参考文章 word字数统计
  • 使用Python将Word文档转换为PDF的方法

    摘要 文介绍了如何使用Python编程语言将Word文档转换为PDF格式的方法 我们将使用python docx和pywin32库来实现这个功能 这些库提供了与Microsoft Word应用程序的交互能力 正文 在现实生活和工作中 我们可

随机推荐

  • 第7章 软件测试(3)

    一晃3天没有学习了 xff0c 昨天的阅读量创立了一个新高 xff0c 内心还是很欢喜的 7 4 2黑盒技术 黑盒技术着重测试软件功能 xff0c 需重点研究需求说明和总体设计中有关程序功能输入 输出之间的关系等信息 xff0c 从而与测试
  • 第七章 软件测试(此章完结)

    春乏秋困 xff0c 一个早上哈气连天 脖子酸 腰痛 xff08 捂脸 xff09 近期叫醒我的不是闹钟也不是梦想 xff0c 而是凌晨4点和6点广播大喇叭喊居民做核酸的声音 xff0c 还是别的小区的 xff08 再次捂脸 xff09 也
  • 第十章:面向对象分析(2)

    3 泛化关系 泛化关系和类找那个的泛化概念是一样的 xff0c 于用例继承父用例的行为和含义 xff0c 还可以增加或覆盖父用例的行为 xff0c 子用例可以出现在任何父用例出现的位置 xff08 父和子均有具体的实例 xff09 也可以重
  • 第十章:面向对象分析(此章完结)

    10 4 4建立活动图 活动具体表现为由一系列动作组成的执行过程 xff0c 将各种活动及不同活动之间的转换 xff0c 用图形进行表示就构成了活动图 xff0c 作用是对系统的行为建模 1 活动图与流程图 活动图描述系统使用的活动 xff
  • 第十五章 软件工程新技术

    俺家老大说这一章我不需要仔细看 xff0c 快快过一遍就行 xff08 可能是觉得以我的能力一时半会也用不到吧 xff08 捂脸 xff09 xff09 那么我就抄一段本章小结吧 xff0c 后面如有需要我在重新认真学习 xff08 奸笑
  • 第四章 软件测试方法(2)

    上周学习了白盒 xff0c 本周开始学习黑盒测试 4 3黑盒测试 黑盒测试 xff08 Black Box Testing xff09 也称功能测试 xff0c 主要测试每个功能是否正常使用 是软件测试使用中最广泛的一类测试 在黑盒测试中
  • vnc viewer手机中文版,超好用的5款vnc viewer手机中文版

    在平时工作中 xff0c 经常会用到vnc viewer软件 当软件打开都是英文介绍 xff0c 真的让人很头痛 在各种各样的vnc viewer手机中文版软件中 xff0c 要想找到那款让你使用方便的软件 xff0c 真的很不容易 xff
  • 第九章 APP项目测试(4) 测试工具

    接上面一篇 继续 xff08 7 xff09 kill process after error 参数说明 xff1a 用于指定当应用程序发生错误时 xff0c 是否停止运行 如果指定此参数 xff0c 当应用程序发生错误时 xff0c 应用
  • 第九章 APP项目测试(此章完结)

    9 4 5 Fiddler 是一个HTTP的调试代理工具 xff0c 它以代码服务器的方式 xff0c 监听系统的HTTP网络数据 xff0c 俗称抓包工具 可直接去官网下载安装 1 Fiddler工具介绍 启动Fiddler后 xff0c
  • 软硬件基础知识学习--小日记(1)

    终于看完了软件工程和软件测试技术指南两本书 xff0c 因为是自学总觉得前学后忘 有时候找老公不耻下问 xff0c 他总是很完美的把我问的哑口无言 昨天意外翻到黑马程序的的视频 xff0c 觉得非常适合我这0基础的小白 然后就有了今天的小日
  • Qt for Windows版本下编译QtDBus模块

    转载时请注明出处和作者联系方式 作者联系方 式 xff1a Lutx lt 80437 at zj dot com gt Qt中已经包含了QtDBus模块 但此模块只能在Unix系统下使用 却不支持Windows系统 这里介绍的是Windo
  • 智安网络丨一行代码,揭开CPU执行原理!

    计算机如何执行你写的代码 xff1f 知乎上有人提问 xff1a 电脑怎样执行编程语言的 xff1f 图片 很多刚刚入坑的小白可能对此完全没有概念 xff0c 或者模模糊糊知道个大概 xff0c 我们写下的一行行代码 xff0c 计算机到底
  • 推荐7个冷门手机APP,每一个都让我相见恨晚

    推荐7个让我相见恨晚的手机APP 1 Smart Kit 360 Smart Kit 360是一个全能的工具箱软件 xff0c 只有10M的大小 xff0c 却提供了40多个实用工具 xff0c 有了它 xff0c 就不需要下载这么多软件了
  • 推荐8款有趣实用的软件,建议你先收藏,总有一天你会用到

    推荐8个非常好用的软件 xff0c 每一个都能给人带来惊喜 xff0c 软件的实用性非常强 xff0c 千万不要错过了 1 央视频 央视频是中央广播电视总台出品的高质量视频社交软件 xff0c 内容丰富 xff0c 功能强大 强大的电视直播
  • 中国天气网 API

    中国天气网 API 真正的中国天气api接口xml xff0c json 详解 前言 某天想写个天气软件 xff0c 于是上网找找有没有免费的天气 API 发现许多的API不是收费 xff0c 就是不能用了 xff08 心塞塞 xff09
  • 【Linux-Ubuntu】apt-get update软件更新的时候经常出错

    1 网络问题 将电脑连接的WIFI改成手机热点连接 2 镜像源问题 使用最新的镜像源进行下载更新 xff1a 可以参考下面方式获取 xff1a 然后选择手动替换 xff0c 或者命令替换 xff0c 一般你直接复制原来的 list文件 xf
  • Flink与Kafka的爱恨情仇

    FlinkKafkaConsumer 源码剖析 FlinkKafkaConsumer 的继承关系如下图所示 可以发现几个版本的 FlinkKafkaConsumer 都继承自 FlinkKafkaConsumerBase 抽象类 xff0c
  • realvnc,简单介绍realvnc

    什么是vnc vnc xff08 Virtual Network Computing xff0c 虚拟网络计算 xff09 最早是一套由英国剑桥大学ATT实验室在2002年开发的轻量型的远程控制计算机软件 xff0c 其采用了 GPL 授权
  • 843. Guess the Word

    Hard 435458Add to ListShare This problem is an interactive problem new to the LeetCode platform We are given a word list
  • 127. Word Ladder

    Given two words beginWord and endWord and a dictionary 39 s word list find the length of shortest transformation sequenc