【LeetCode】318. 最大单词长度乘积

318. 最大单词长度乘积

难度:中等

题目

给你一个字符串数组 words ,找出并返回 length(words[i]) * length(words[j]) 的最大值,并且这两个单词不含有公共字母。如果不存在这样的两个单词,返回 0

示例 1:

输入:words = ["abcw","baz","foo","bar","xtfn","abcdef"]
输出:16 
解释:这两个单词为 "abcw", "xtfn"。

示例 2:

输入:words = ["a","ab","abc","d","cd","bcd","abcd"]
输出:4 
解释:这两个单词为 "ab", "cd"。

示例 3:

输入:words = ["a","aa","aaa","aaaa"]
输出:0 
解释:不存在这样的两个单词。

提示:

  • 2 <= words.length <= 1000
  • 1 <= words[i].length <= 1000
  • words[i] 仅包含小写字母

个人题解

思路:

  1. 为了得到最大单词长度乘积,遍历字符串数组 words 中的每一对单词,判断这一对单词是否有公共字母,如果没有公共字母,则用这一对单词的长度乘积更新最大单词长度乘积。
  2. 对上述情况剪枝,只有长度乘积更大的才需要判断是否有公共字母
class Solution {// 入口public int maxProduct(String[] words) {int max = 0;int len = words.length;for (int i = 0; i < len; i++) {for (int j = i + 1; j < len; j++) {if (words[i].length() * words[j].length() > max && !hasRepeatChar(words[i], words[j])) {max = words[i].length() * words[j].length();}}}return max;}public boolean hasRepeatChar(String s1, String s2) {int s1Len = s1.length();int s2Len = s2.length();HashSet<Character> hashSet = new HashSet<>(Math.min(s1Len, 26));for (int i = 0; i < s1Len; i++) {hashSet.add(s1.charAt(i));}for (int i = 0; i < s2Len; i++) {if (hashSet.contains(s2.charAt(i))) {return true;}}return false;}
}

官方题解

方法一:位运算

如果可以将判断两个单词是否有公共字母的时间复杂度降低到 O(1),则可以将总时间复杂度降低到 O(n^2)。可以使用位运算预处理每个单词,通过位运算操作判断两个单词是否有公共字母。由于单词只包含小写字母,共有 26 个小写字母,因此可以使用位掩码的最低 26 位分别表示每个字母是否在这个单词中出现。将 a 到 z 分别记为第 0 个字母到第 25 个字母,则位掩码的从低到高的第 i 位是 1 当且仅当第 i 个字母在这个单词中,其中 0 <= i <= 25。

用数组 masks 记录每个单词的位掩码表示。计算数组 masks 之后,判断第 i 个单词和第 j 个单词是否有公共字母可以通过判断 mask[i] & mask[j] 是否等于 0 实现,当且仅当 mask[i] & mask[j] = 0 时,第 i 个单词和第 j 个单词没有公共字母,此时使用这两个单词的长度乘积更新最大单词长度乘积。

class Solution {public int maxProduct(String[] words) {int length = words.length;int[] masks = new int[length];for (int i = 0; i < length; i++) {String word = words[i];int wordLength = word.length();for (int j = 0; j < wordLength; j++) {masks[i] |= 1 << (word.charAt(j) - 'a');}}int maxProd = 0;for (int i = 0; i < length; i++) {for (int j = i + 1; j < length; j++) {if ((masks[i] & masks[j]) == 0) {maxProd = Math.max(maxProd, words[i].length() * words[j].length());}}}return maxProd;}
}

复杂度分析

  • 时间复杂度:O(L + n^2),其中L是数组 words 中的全部单词长度之和,n是数组 words 的长度
  • 空间复杂度:O(n),n是数组 words 的长度。需要常见长度为 n 的位掩码数组 masks
方法二:位运算优化

方法一需要对数组 words 中的每个单词计算位掩码,如果数组 words 中存在由相同的字母组成的不同单词,则会造成不必要的重复计算。例如单词 mcct 和 mct 包含的字母相同,只是字母的出现次数和单词长度不同,因此这两个单词的位掩码表示也相同。由于判断两个单词是否有公共字母是通过判断两个单词的位掩码的按位与运算实现,因此在位掩码相同的情况下,单词的长度不会影响是否有公共字母,当两个位掩码的按位与运算等于 0 时,为了得到最大单词长度乘积,这两个位掩码对应的单词长度应该尽可能大。根据上述分析可知,如果有多个单词的位掩码相同,则只需要记录该位掩码对应的最大单词长度即可。

可以使用哈希表记录每个位掩码对应的最大单词长度,然后遍历哈希表中的每一对位掩码,如果这一对位掩码的按位与运算等于 0 ,则用这一对位掩码对应的长度乘积更新最大单词长度乘积。

由于每个单词的位掩码都不等于 0 ,任何一个不等于 0 的数和自身做按位与运算的结果一定不等于 0 ,因此当一对位掩码的按位与运算等于 0 时,这两个位掩码一定是不同的,对应的单词也一定是不同的。

class Solution {public int maxProduct(String[] words) {Map<Integer, Integer> map = new HashMap<Integer, Integer>();int length = words.length;for (int i = 0; i < length; i++) {int mask = 0;String word = words[i];int wordLength = word.length();for (int j = 0; j < wordLength; j++) {mask |= 1 << (word.charAt(j) - 'a');}if (wordLength > map.getOrDefault(mask, 0)) {map.put(mask, wordLength);}}int maxProd = 0;Set<Integer> maskSet = map.keySet();for (int mask1 : maskSet) {int wordLength1 = map.get(mask1);for (int mask2 : maskSet) {if ((mask1 & mask2) == 0) {int wordLength2 = map.get(mask2);maxProd = Math.max(maxProd, wordLength1 * wordLength2);}}}return maxProd;}
}

复杂度分析

  • 时间复杂度:O(L + n ^ 2)
  • 空间复杂度:O(n)

作者:力扣官方题解
链接:https://leetcode.cn/problems/maximum-product-of-word-lengths/solutions/1104441/zui-da-dan-ci-chang-du-cheng-ji-by-leetc-lym9/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

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

相关文章

js深度学习(三)

循环 var i0 for(;i<10;){ console.log(i) i } while(i<10){ console.log(i) i } var i100; for(;i--;){ console.log(i) }2、引用值 typeof&#xff1a;number string boolean Object(object/array/null出现是为了指定为空对象/)undefined function typeof a >unde…

2021年电工杯数学建模B题光伏建筑一体化板块指数发展趋势分析及预测求解全过程论文及程序

2021年电工杯数学建模 B题 光伏建筑一体化板块指数发展趋势分析及预测 原题再现&#xff1a; 国家《第十四个五年规划和 2035 年远景目标纲要》中提出&#xff0c;将 2030 年实现“碳达峰”与 2060 年实现“碳中和”作为我国应对全球气候变暖的一个重要远景目标。光伏建筑一体…

如何对ppt文件设置修改权限?

PPT文件会应用在会议、演讲、课件等工作生活中&#xff0c;当我们制作好了PPT之后&#xff0c;保护内容防止在演示时出错是很重要的&#xff0c;那么如何将PPT文件设置成禁止修改模式呢&#xff1f;今天分享几个方法给大家。 方法一 将PPT文件直接保存或者另存为一份文件&…

微信小程序文件上传wx.uploadFile

网页版查看了一下负载要求是这样 wx.uploadFile({url: ${wx.getStorageSync(apiUrl)}//sysFileInfo/upload?token${wx.getStorageSync(token)}, // 仅为示例&#xff0c;非真实的接口地址filePath: files[0].url,name: file,formData: {secretFlag: Y },success: (res) > {…

【漏洞复现】Django _2.0.8_任意URL跳转漏洞(CVE-2018-14574)

感谢互联网提供分享知识与智慧&#xff0c;在法治的社会里&#xff0c;请遵守有关法律法规 文章目录 1.1、漏洞描述1.2、漏洞等级1.3、影响版本1.4、漏洞复现1、基础环境2、漏洞扫描3、漏洞验证 1.5、修复建议 说明内容漏洞编号CVE-2018-14574漏洞名称Django任意URL跳转漏洞漏洞…

力扣(LeetCode)容器装水问题

题目描述 给你 n 个非负整数 a1&#xff0c;a2&#xff0c;...&#xff0c;an&#xff0c;每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线&#xff0c;垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可…

LeetCode|动态规划|392. 判断子序列、115. 不同的子序列、 583. 两个字符串的删除操作

目录 一、392. 判断子序列 1.题目描述 2.解题思路 3.代码实现(双指针解法) 4.代码实现&#xff08;动态规划解法&#xff09; 二、115. 不同的子序列 1.题目描述 2.解题思路 3.代码实现&#xff08;C语言版本&#xff09; 4.代码实现&#xff08;C版本&#xff09; …

PyTorch入门学习(十二):神经网络-搭建小实战和Sequential的使用

目录 一、介绍 二、先决条件 三、代码解释 一、介绍 在深度学习领域&#xff0c;构建复杂的神经网络模型可能是一项艰巨的任务&#xff0c;尤其是当您有许多层和操作需要组织时。幸运的是&#xff0c;PyTorch提供了一个方便的工具&#xff0c;称为Sequential API&#xff0c…

R语言piecewiseSEM结构方程模型在生态环境领域实践技术应用

结构方程模型&#xff08;Sructural Equation Modeling&#xff0c;SEM&#xff09;可分析系统内变量间的相互关系&#xff0c;并通过图形化方式清晰展示系统中多变量因果关系网&#xff0c;具有强大的数据分析功能和广泛的适用性&#xff0c;是近年来生态、进化、环境、地学、…

「Verilog学习笔记」异步复位的串联T触发器

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点&#xff0c;刷题网站用的是牛客网 分析 这道题目里我们有两个需要明确的点&#xff1a; 1. 什么是异步复位 2. 什么是串联的T触发器 关于第一个点&#xff0c;可以看我的这篇文章&#xff0c;已经整理好了&a…

通过GFlags工具来复现因为野指针、内存越界等造成的程序崩溃

系列文章目录 C程序异常调查专栏 文章目录 系列文章目录前言一、GFlags是什么&#xff1f;二、如何获取GFlags三、使用步骤1.确认GFlags是否已经安装2.以管理员权限启动Command prompt3.GFlags有效设定4.检查GFlags有效设定是否成功5.根据客户复现步骤运行程序 总结 前言 客户…

无线发射芯片解决方案在智能家居中的应用

随着物联网的发展&#xff0c;智能家居已经成为一个热门话题。智能家居利用无线技术来实现设备之间的互联互通&#xff0c;提供更智能、更便利的生活体验。无线发射芯片解决方案在智能家居中扮演着关键的角色&#xff0c;它们为智能家居设备之间的通信提供了稳定、高效的连接&a…

RabbitMQ(高级特性) 设置队列所有消息存活时间

RabbitMQ可以设置消息的存活时间&#xff08;Time To Live&#xff0c;简称TTL&#xff09;&#xff0c;当消息到达存活时间后还没有被消费&#xff0c;会被移出队列。RabbitMQ可以对队列的所有消息设置存活时间&#xff0c;也可以对某条消息设置存活时间。 Configuration pub…

cronet 库各个调用栈记录

处理 alt-svc 头&#xff1a; * frame #0: dc64e44 net::HttpStreamFactory::ProcessAlternativeServices(this0x0000000282c37b10, session0x0000000138822400, network_isolation_key0x00000001369bf390, headers0x00000002824d5220, http_server0x000000016e3c0590) at ht…

测试用例的设计方法(全):判定表驱动分析方法

目录 判定表驱动分析方法 一. 方法简介 二. 实战演习 判定表驱动分析方法 一. 方法简介 1.定义&#xff1a;判定表是分析和表达多逻辑条件下执行不同操作的情况的工具。 2.判定表的优点 能够将复杂的问题按照各种可能的情况全部列举出来&#xff0c;简明并避免遗漏。因此…

Spring基础(1):两个概念

最近看了点Spring的源码&#xff0c;于是来稍微扯一扯&#xff0c;希望能帮一部分培训班出身的朋友撕开一道口子&#xff0c;透透气。 广义上的Spring指的是Spring整个项目&#xff0c;包含SpringBoot、SpringCloud、SpringFramework、SpringData等等&#xff0c; 本系列文章…

Java快速排序算法、三路快排(Java算法和数据结构总结笔记)[7/20]

一、什么是快速排序算法 快速排序的基本思想是选择一个基准元素&#xff08;通常选择最后一个元素&#xff09;将数组分割为两部分&#xff0c;一部分小于基准元素&#xff0c;一部分大于基准元素。 然后递归地对两部分进行排序&#xff0c;直到整个数组有序。这个过程通过 par…

浅谈前端自定义VectorGrid矢量瓦片样式

目录 前言 一、VectorGrid相关API介绍 1、VectorGrid 2、 LayerStyles样式详解 二、样式自动配置 1、页面定义 2、地图及PBF瓦片引入 3、矢量瓦片样式定义 4、鼠标事件交互 三、最终效果 1、自定义样式展示 2、鼠标交互 总结 前言 在上一篇博客中&#xff0c;详细讲…

C#完成XML文档节点的自动计算功能

一个项目涉及XML文档中节点的自动计算&#xff0c;就是XML文档的每个节点都参与运算&#xff0c;要求&#xff1a; ⑴如果节点有计算公式则按照计算公式进行&#xff1b; ⑵如果节点没有计算公式则该节点的值就是所有子节点的值之和&#xff1b; ⑶节点有4种类型&#xff0c;计…

数据结构——B树

文章目录 B树1. 概念2. B树插入分析3.插入过程4. B树插入实现5.B树验证6. B树性能分析7.B树&B*树8. 小结9. B树的运用MyISAMInnoDB 10. 总结 B树 可以用于查询的数据结构非常的多&#xff0c;比如说二插搜索树、平衡树、哈希表、位图、布隆过滤器&#xff0c;但如果需要存…