LeetCode第244题_最短单词距离II

news2025/6/11 11:31:48

LeetCode第244题:最短单词距离II

问题描述

设计一个类,接收一个单词数组 wordsDict,并实现一个方法,该方法能够计算两个不同单词在该数组中出现位置的最短距离。

你需要实现一个 WordDistance 类:

  • WordDistance(String[] wordsDict) 初始化对象,用单词数组 wordsDict 初始化对象。
  • int shortest(String word1, String word2) 返回数组 wordsDict 中两个单词 word1word2 的最短距离。

注意:你可以假设 word1word2 都在 wordsDict 中,且它们是不同的单词。

难度:中等

示例

输入: 
["WordDistance", "shortest", "shortest"]
[[["practice", "makes", "perfect", "coding", "makes"]], ["coding", "practice"], ["makes", "coding"]]
输出: [null, 3, 1]

解释:
WordDistance wordDistance = new WordDistance(["practice", "makes", "perfect", "coding", "makes"]);
wordDistance.shortest("coding", "practice"); // 返回 3
wordDistance.shortest("makes", "coding");    // 返回 1

约束条件

  • 1 <= wordsDict.length <= 3 * 10^4
  • 1 <= wordsDict[i].length <= 10
  • wordsDict[i] 由小写英文字母组成
  • word1word2wordsDict 中,且它们是不同的单词
  • shortest 操作次数不超过 5000

解题思路

这个问题是 LeetCode 243 “最短单词距离” 的进阶版本。主要区别在于,本题需要设计一个类,可以重复查询不同单词对之间的最短距离。关键在于如何有效地预处理数据,以便快速响应查询请求。

我们可以采用以下两种主要方法:

方法一:预处理位置索引

这种方法在初始化时,将每个单词在数组中的所有位置存储在一个哈希表中:

  1. 构造函数中,遍历单词数组 wordsDict,记录每个单词出现的所有位置
  2. 对于 shortest 方法,获取两个单词的位置列表,然后计算它们之间的最短距离
  3. 为了计算最短距离,我们比较两个位置列表中的每对位置,找到最小的绝对差值

方法二:双指针优化

方法一在计算最短距离时可能会做很多不必要的比较。考虑到位置列表是有序的(因为我们按顺序遍历数组),我们可以使用双指针方法优化距离计算:

  1. 初始化两个指针,分别指向两个位置列表的开始
  2. 比较当前两个指针指向的位置,计算它们的距离
  3. 移动指向较小位置的指针
  4. 重复步骤 2 和 3,直到其中一个指针到达列表末尾
  5. 返回在此过程中找到的最小距离

这种方法避免了不必要的比较,复杂度从 O(m*n) 降低到 O(m+n),其中 m 和 n 是两个单词在数组中出现的次数。

代码实现

方法一:预处理位置索引

C#实现
public class WordDistance {
    private Dictionary<string, List<int>> wordToIndices;

    public WordDistance(string[] wordsDict) {
        wordToIndices = new Dictionary<string, List<int>>();
        
        // 预处理每个单词的位置
        for (int i = 0; i < wordsDict.Length; i++) {
            if (!wordToIndices.ContainsKey(wordsDict[i])) {
                wordToIndices[wordsDict[i]] = new List<int>();
            }
            wordToIndices[wordsDict[i]].Add(i);
        }
    }
    
    public int Shortest(string word1, string word2) {
        List<int> indices1 = wordToIndices[word1];
        List<int> indices2 = wordToIndices[word2];
        
        int minDistance = int.MaxValue;
        
        // 计算两个位置列表中的最短距离
        foreach (int idx1 in indices1) {
            foreach (int idx2 in indices2) {
                minDistance = Math.Min(minDistance, Math.Abs(idx1 - idx2));
            }
        }
        
        return minDistance;
    }
}
Python实现
class WordDistance:
    def __init__(self, wordsDict: List[str]):
        self.locations = {}
        
        # 预处理每个单词的位置
        for i, word in enumerate(wordsDict):
            if word not in self.locations:
                self.locations[word] = []
            self.locations[word].append(i)

    def shortest(self, word1: str, word2: str) -> int:
        locations1 = self.locations[word1]
        locations2 = self.locations[word2]
        
        min_dist = float('inf')
        
        # 计算两个位置列表中的最短距离
        for loc1 in locations1:
            for loc2 in locations2:
                min_dist = min(min_dist, abs(loc1 - loc2))
                
        return min_dist
C++实现
class WordDistance {
private:
    unordered_map<string, vector<int>> wordToIndices;
    
public:
    WordDistance(vector<string>& wordsDict) {
        // 预处理每个单词的位置
        for (int i = 0; i < wordsDict.size(); i++) {
            wordToIndices[wordsDict[i]].push_back(i);
        }
    }
    
    int shortest(string word1, string word2) {
        vector<int>& indices1 = wordToIndices[word1];
        vector<int>& indices2 = wordToIndices[word2];
        
        int minDistance = INT_MAX;
        
        // 计算两个位置列表中的最短距离
        for (int idx1 : indices1) {
            for (int idx2 : indices2) {
                minDistance = min(minDistance, abs(idx1 - idx2));
            }
        }
        
        return minDistance;
    }
};

方法二:双指针优化

C#实现
public class WordDistance {
    private Dictionary<string, List<int>> wordToIndices;

    public WordDistance(string[] wordsDict) {
        wordToIndices = new Dictionary<string, List<int>>();
        
        // 预处理每个单词的位置
        for (int i = 0; i < wordsDict.Length; i++) {
            if (!wordToIndices.ContainsKey(wordsDict[i])) {
                wordToIndices[wordsDict[i]] = new List<int>();
            }
            wordToIndices[wordsDict[i]].Add(i);
        }
    }
    
    public int Shortest(string word1, string word2) {
        List<int> indices1 = wordToIndices[word1];
        List<int> indices2 = wordToIndices[word2];
        
        int i = 0, j = 0;
        int minDistance = int.MaxValue;
        
        // 使用双指针计算最短距离
        while (i < indices1.Count && j < indices2.Count) {
            int idx1 = indices1[i];
            int idx2 = indices2[j];
            
            minDistance = Math.Min(minDistance, Math.Abs(idx1 - idx2));
            
            // 移动指向较小位置的指针
            if (idx1 < idx2) {
                i++;
            } else {
                j++;
            }
        }
        
        return minDistance;
    }
}
Python实现
class WordDistance:
    def __init__(self, wordsDict: List[str]):
        self.locations = {}
        
        # 预处理每个单词的位置
        for i, word in enumerate(wordsDict):
            if word not in self.locations:
                self.locations[word] = []
            self.locations[word].append(i)

    def shortest(self, word1: str, word2: str) -> int:
        locations1 = self.locations[word1]
        locations2 = self.locations[word2]
        
        i, j = 0, 0
        min_dist = float('inf')
        
        # 使用双指针计算最短距离
        while i < len(locations1) and j < len(locations2):
            min_dist = min(min_dist, abs(locations1[i] - locations2[j]))
            
            # 移动指向较小位置的指针
            if locations1[i] < locations2[j]:
                i += 1
            else:
                j += 1
                
        return min_dist
C++实现
class WordDistance {
private:
    unordered_map<string, vector<int>> wordToIndices;
    
public:
    WordDistance(vector<string>& wordsDict) {
        // 预处理每个单词的位置
        for (int i = 0; i < wordsDict.size(); i++) {
            wordToIndices[wordsDict[i]].push_back(i);
        }
    }
    
    int shortest(string word1, string word2) {
        vector<int>& indices1 = wordToIndices[word1];
        vector<int>& indices2 = wordToIndices[word2];
        
        int i = 0, j = 0;
        int minDistance = INT_MAX;
        
        // 使用双指针计算最短距离
        while (i < indices1.size() && j < indices2.size()) {
            int idx1 = indices1[i];
            int idx2 = indices2[j];
            
            minDistance = min(minDistance, abs(idx1 - idx2));
            
            // 移动指向较小位置的指针
            if (idx1 < idx2) {
                i++;
            } else {
                j++;
            }
        }
        
        return minDistance;
    }
};

性能分析

方法一:预处理位置索引

  • 初始化时间复杂度:O(n),其中 n 是 wordsDict 的长度。我们需要遍历整个数组一次来建立索引。
  • 查询时间复杂度:O(m * k),其中 m 和 k 分别是 word1 和 word2 在数组中出现的次数。在最坏情况下,我们需要比较每对位置。
  • 空间复杂度:O(n),用于存储所有单词的位置索引。

方法二:双指针优化

  • 初始化时间复杂度:O(n),与方法一相同。
  • 查询时间复杂度:O(m + k),其中 m 和 k 分别是 word1 和 word2 在数组中出现的次数。使用双指针方法,我们只需遍历两个位置列表一次。
  • 空间复杂度:O(n),与方法一相同。

方法对比

方法初始化时间查询时间空间复杂度优势劣势
预处理位置索引O(n)O(m * k)O(n)实现简单直观查询效率较低
双指针优化O(n)O(m + k)O(n)查询效率高无明显劣势

设计思路分析

  1. 空间与时间权衡:该设计在初始化时预先计算并存储所有单词的位置,牺牲一些空间来换取更快的查询速度。这适用于查询次数远多于单词数量的情况。

  2. 双指针技巧:利用位置列表的有序性,通过双指针算法将查询复杂度从 O(m*k) 降低到 O(m+k),显著提高了查询效率。

  3. 哈希表索引:使用哈希表按单词索引位置列表,实现了 O(1) 时间的位置列表查找。

相关题目

  • LeetCode 第243题:最短单词距离
  • LeetCode 第245题:最短单词距离 III
  • LeetCode 第249题:移位字符串分组

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2406193.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python3GUI--基于PyQt5+DeepSort+YOLOv8智能人员入侵检测系统(详细图文介绍)

文章目录 一&#xff0e;前言二&#xff0e;技术介绍1.PyQt52.DeepSort3.卡尔曼滤波4.YOLOv85.SQLite36.多线程7.入侵人员检测8.ROI区域 三&#xff0e;核心功能1.登录注册1.登录2.注册 2.主界面1.主界面简介2.数据输入3.参数配置4.告警配置5.操作控制台6.核心内容显示区域7.检…

5. TypeScript 类型缩小

在 TypeScript 中&#xff0c;类型缩小&#xff08;Narrowing&#xff09;是指根据特定条件将变量的类型细化为更具体的过程。它帮助开发者编写更精确、更准确的代码&#xff0c;确保变量在运行时只以符合其类型的方式进行处理。 一、instanceof 缩小类型 TypeScript 中的 in…

【QT】qtdesigner中将控件提升为自定义控件后,css设置样式不生效(已解决,图文详情)

目录 0.背景 1.解决思路 2.详细代码 0.背景 实际项目中遇到的问题&#xff0c;描述如下&#xff1a; 我在qtdesigner用界面拖了一个QTableView控件&#xff0c;object name为【tableView_electrode】&#xff0c;然后【提升为】了自定义的类【Steer_Electrode_Table】&…

【Docker 02】Docker 安装

&#x1f308; 一、各版本的平台支持情况 ⭐ 1. Server 版本 Server 版本的 Docker 就只有个命令行&#xff0c;没有界面。 Platformx86_64 / amd64arm64 / aarch64arm(32 - bit)s390xCentOs√√Debian√√√Fedora√√Raspbian√RHEL√SLES√Ubuntu√√√√Binaries√√√ …

论文笔记:Large Language Models for Next Point-of-Interest Recommendation

SIGIR 2024 1 intro 传统的基于数值的POI推荐方法在处理上下文信息时存在两个主要限制 需要将异构的LBSN数据转换为数字&#xff0c;这可能导致上下文信息的固有含义丢失仅依赖于统计和人为设计来理解上下文信息&#xff0c;缺乏对上下文信息提供的语义概念的理解 ——>使用…

LeetCode 2894.分类求和并作差

目录 题目&#xff1a; 题目描述&#xff1a; 题目链接&#xff1a; 思路&#xff1a; 思路一详解&#xff08;遍历 判断&#xff09;&#xff1a; 思路二详解&#xff08;数学规律/公式&#xff09;&#xff1a; 代码&#xff1a; Java思路一&#xff08;遍历 判断&a…

CCF 开源发展委员会 “开源高校行“ 暨红山开源 + OpenAtom openKylin 高校行活动在西安四所高校成功举办

点击蓝字 关注我们 CCF Opensource Development Committee CCF开源高校行 暨红山开源 openKylin 高校行 西安站 5 月 26 日至 28 日&#xff0c;CCF 开源发展委员会 "开源高校行" 暨红山开源 OpenAtom openKylin 高校行活动在西安四所高校&#xff08;西安交通大学…

RabbitMQ work模型

Work 模型是 RabbitMQ 最基础的消息处理模式&#xff0c;核心思想是 ​​多个消费者竞争消费同一个队列中的消息​​&#xff0c;适用于任务分发和负载均衡场景。同一个消息只会被一个消费者处理。 当一个消息队列绑定了多个消费者&#xff0c;每个消息消费的个数都是平摊的&a…

基于微信小程序的作业管理系统源码数据库文档

作业管理系统 摘 要 随着社会的发展&#xff0c;社会的方方面面都在利用信息化时代的优势。互联网的优势和普及使得各种系统的开发成为必需。 本文以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#xff0c;它主要是采用java语言技术和微信小程序来完成对系统的…

CSS(2)

文章目录 Emmet语法快速生成HTML结构语法 Snipaste快速生成CSS样式语法快速格式化代码 快捷键&#xff08;VScode&#xff09;CSS 的复合选择器什么是复合选择器交集选择器后代选择器(重要)子选择器(重要&#xff09;并集选择器(重要&#xff09;**链接伪类选择器**focus伪类选…

【RabbitMQ】- Channel和Delivery Tag机制

在 RabbitMQ 的消费者代码中&#xff0c;Channel 和 tag 参数的存在是为了实现消息确认机制&#xff08;Acknowledgment&#xff09;和精细化的消息控制。 Channel 参数 作用 Channel 是 AMQP 协议的核心操作接口&#xff0c;通过它可以直接与 RabbitMQ 交互&#xff1a; 手…

NLP学习路线图(三十四): 命名实体识别(NER)

一、命名实体识别(NER)是什么? 命名实体识别(Named Entity Recognition, NER)是自然语言处理中的一项关键序列标注任务。其核心目标是从非结构化的文本中自动识别出特定类别的名词性短语,并将其归类到预定义的类别中。 核心目标:找到文本中提到的命名实体,并分类。 典…

【HTML】HTML 与 CSS 基础教程

作为 Java 工程师&#xff0c;掌握 HTML 和 CSS 也是需要的&#xff0c;它能让你高效与前端团队协作、调试页面元素&#xff0c;甚至独立完成简单页面开发。本文将用最简洁的方式带你掌握核心概念。 一、HTML&#xff0c;网页骨架搭建 核心概念&#xff1a;HTML通过标签定义内…

Windows开机自动启动中间件

WinSW&#xff08;Windows Service Wrapper 是一个开源的 Windows 服务包装器&#xff0c;它可以帮助你将应用程序打包成系统服务&#xff0c;并实现开机自启动的功能。 一、下载 WinSW 下载 WinSW-x64.exe v2.12.0 (⬇️ 更多版本下载) 和 sample-minimal.xml 二、配置 WinS…

【图片转AR场景】Tripo + Blender + Kivicube 实现图片转 AR 建模

总览 1.将 2D 图片转为立体建模 2. 3. 一、将 2D 图片转为立体建模 1.工具介绍 Tripo 网站 2.找图片 找的图片必须是看起来能够让 AI 有能力识别和推理的&#xff0c;因为现在的AI虽然可以补全但是能力还没有像人的想象力那么丰富。 比如上面这张图片&#xff0c;看起来虽…

LSTM-XGBoost多变量时序预测(Matlab完整源码和数据)

LSTM-XGBoost多变量时序预测&#xff08;Matlab完整源码和数据&#xff09; 目录 LSTM-XGBoost多变量时序预测&#xff08;Matlab完整源码和数据&#xff09;效果一览基本介绍程序设计参考资料 效果一览 基本介绍 普通的多变量时序已经用腻了&#xff0c;审稿人也看烦了&#…

C#学习12——预处理

一、预处理指令&#xff1a; 解释&#xff1a;是在编译前由预处理器执行的命令&#xff0c;用于控制编译过程。这些命令以 # 开头&#xff0c;每行只能有一个预处理指令&#xff0c;且不能包含在方法或类中。 个人理解&#xff1a;就是游戏里面的备战阶段&#xff08;不同对局…

Razor编程中@Helper的用法大全

文章目录 第一章&#xff1a;Helper基础概念1.1 Helper的定义与作用1.2 Helper的基本语法结构1.3 Helper与HtmlHelper的区别 第二章&#xff1a;基础Helper用法2.1 无参数Helper2.2 带简单参数的Helper2.3 带默认值的参数2.4 使用模型作为参数 第三章&#xff1a;高级Helper用法…

鸿蒙APP测试实战:从HDC命令到专项测试

普通APP的测试与鸿蒙APP的测试有一些共同的特征&#xff0c;但是也有一些区别&#xff0c;其中共同特征是&#xff0c;它们都可以通过cmd的命令提示符工具来进行app的性能测试。 其中区别主要是&#xff0c;对于稳定性测试的命令的区别&#xff0c;性能指标获取方式的命令的区…

HarmonyOS-ArkUI 自定义弹窗

自定义弹窗 自定义弹窗是界面开发中最为常用的一种弹窗写法。在自定义弹窗中&#xff0c; 布局样式完全由您决定&#xff0c;非常灵活。通常会被封装成工具类&#xff0c;以使得APP中所有弹窗具备相同的设计风格。 自定义弹窗具备的能力有 打开弹窗自定义布局&#xff0c;以…