一站式指南:第 377 场力扣周赛的终极题解

比赛详情

比赛地址

  • 题目一很简单
  • 题目二主要是题目长了点,其实解法很常规(比赛后才意识到)
  • 题目三套用Dijkstra算法
  • 题目四没时间解答水平还有待提升(其实就是需要灵活组合运用已知的算法,有点类似大模型的Agent)

题解和思路

第一题:最小数字游戏 

class Solution:def numberGame(self, nums: List[int]) -> List[int]:nums.sort()arr = []# 每一轮中,Alice 先移除最小元素,然后 Bob 移除最小元素while nums:# Alice 移除最小元素alice_num = nums.pop(0) if nums else None# Bob 移除最小元素bob_num = nums.pop(0) if nums else None# Bob 先将他的元素添加到 arrif bob_num is not None:arr.append(bob_num)# 然后 Alice 添加她的元素if alice_num is not None:arr.append(alice_num)return arr

第二题:移除栅栏得到的正方形田地的最大面积 

把横纵所有的长度存进set,再遍历两个set,相等的话再和之前的最大面积比较

from typing import Listclass Solution:def maximizeSquareArea(self, m: int, n: int, hFences: List[int], vFences: List[int]) -> int:mod = 10 ** 9 + 7def getGaps(fences, size):fences = sorted([1] + fences + [size])  # Include start and end fence positions.combinations = set()for i in range(len(fences)):for j in range(i):combinations.add(fences[i] - fences[j])  # Calculate gaps directly.return combinationshGaps = getGaps(hFences, m)vGaps = getGaps(vFences, n)ans = -1for gap in hGaps:if gap in vGaps:ans = max(ans, gap ** 2)return -1 if ans == -1 else ans % mod

第三题:转换字符串的最小成本 I

import heapq
from typing import List
from collections import defaultdictclass Solution:def minimumCost(self, source: str, target: str, original: List[str], changed: List[str], cost: List[int]) -> int:# 构建图graph = defaultdict(dict)for o, c, co in zip(original, changed, cost):if c not in graph[o] or graph[o][c] > co:graph[o][c] = co# 缓存用于存储已计算的转换成本cost_cache = {}# Dijkstra 算法def dijkstra(src, tgt):if src == tgt:return 0if (src, tgt) in cost_cache:return cost_cache[(src, tgt)]visited = set()min_heap = [(0, src)]  # (cost, node)while min_heap:curr_cost, curr_node = heapq.heappop(min_heap)if curr_node in visited:continuevisited.add(curr_node)if curr_node == tgt:cost_cache[(src, tgt)] = curr_costreturn curr_costfor neighbor, edge_cost in graph[curr_node].items():if neighbor not in visited:heapq.heappush(min_heap, (curr_cost + edge_cost, neighbor))cost_cache[(src, tgt)] = float('inf')return float('inf')# 计算总成本total_cost = 0for s, t in zip(source, target):cost = dijkstra(s, t)if cost == float('inf'):return -1total_cost += costreturn total_cost

第四题:转换字符串的最小成本 II

这道题目的总体思路是解决一个字符串替换问题,其中目标是找出将给定的源字符串 source 转换为目标字符串 target 的最小成本。解决这个问题的方法涉及几个关键步骤:

  1. 字符串编码:使用 Trie 树来处理字符串集合。Trie 树是一种有效的数据结构,用于存储和检索字符串集合。在这个场景中,每个节点代表一个字符,路径从根到特定节点表示一个字符串。通过这种方式,可以为 originalchanged 数组中的每个字符串分配一个唯一的数字标识符。

  2. 构建图并计算最短路径:在 Trie 树的帮助下,为每一对原始和更改后的字符串建立一个有向图,并将转换成本作为边的权重。使用 Dijkstra 算法来计算图中任意两点之间的最短路径,这是为了找出将任意 original 字符串替换为任意 changed 字符串的最小成本。

  3. 动态规划(DFS):通过动态规划来确定将 source 转换为 target 的最小总成本。这一步涉及到递归地检查每个字符和每个可能的字符串替换,来计算不同替换方案的累积成本。动态规划通过记忆化(缓存中间结果)来提高效率,避免重复计算相同的子问题。

  4. 处理特殊情况:代码中还需要处理一些特殊情况,如当 sourcetarget 在某个位置上的字符已经相等时,无需进行替换。

总体而言,这个问题的解决方案是一个综合应用 Trie 树、图论(特别是最短路径算法)和动态规划的复杂算法。它需要精确地处理字符串之间的关系和转换成本,以找出将一个字符串转换为另一个字符串的最小成本路径。

import heapq
from typing import List# TrieNode 是 Trie 树的节点,每个节点存储了指向其子节点的引用和该节点所表示字符串的唯一标识符(sid)
class TrieNode:def __init__(self):# 存储 26 个字母的子节self.children = [None] * 26# 字符串的唯一标识符self.sid = -1# Trie 类是用于存储字符串集合的 Trie 树的实现。它提供了 put 方法来将字符串添加到 Trie 树中,并分配或检索字符串的唯一标识符。
class Trie:def __init__(self):# Trie 树的根节点self.root = TrieNode()# 用于给新字符串分配唯一标识符self.sid = 0def put(self, s):# 将字符串 `s` 添加到 Trie 树中,并返回其唯一标识符# 如果字符串已存在,则返回其现有标识符node = self.rootfor ch in s:index = ord(ch) - ord('a')if not node.children[index]:node.children[index] = TrieNode()node = node.children[index]if node.sid < 0:node.sid = self.sidself.sid += 1return node.sidclass Dijkstra:def __init__(self, size):# 图中节点的数量self.size = size# 图的邻接表表示self.graph = [[] for _ in range(size)]def add_edge(self, u, v, weight):# 向图中添加一条边从 `u` 到 `v`,权重为 `weight`self.graph[u].append((v, weight))def shortest_path(self, start):distances = [float('inf')] * self.sizedistances[start] = 0pq = [(0, start)]while pq:current_distance, current_vertex = heapq.heappop(pq)if current_distance > distances[current_vertex]:continuefor neighbor, weight in self.graph[current_vertex]:distance = current_distance + weightif distance < distances[neighbor]:distances[neighbor] = distanceheapq.heappush(pq, (distance, neighbor))return distancesclass Solution:# 计算将字符串 `source` 转换为 `target` 的最小成本# 使用 Trie 树来存储 `original` 和 `changed` 中的字符串及其唯一标识符# 使用 Dijkstra 算法计算字符串之间的最短转换路径# 使用动态规划(通过 `dfs` 函数)来寻找最小成本的转换序列def minimumCost(self, source: str, target: str, original: List[str], changed: List[str], cost: List[int]) -> int:inf = 1e13trie = Trie()m = len(cost)# 初始化 Dijkstra 算法dijkstra_solver = Dijkstra(m * 2)for i, c in enumerate(cost):x = trie.put(original[i])y = trie.put(changed[i])dijkstra_solver.add_edge(x, y,min(c, dijkstra_solver.graph[x][y] if y in dijkstra_solver.graph[x] else inf))# 计算最短路径shortest_paths = [dijkstra_solver.shortest_path(i) for i in range(trie.sid)]# 动态规划n = len(source)memo = [-1] * n# dfs 是一个辅助递归函数,用于动态规划。它计算从当前索引 i 开始,# 将 source 的剩余部分转换为 target 的最小成本。def dfs(i):# 递归函数,用于计算将 source[i:] 转换为 target[i:] 的最小成本if i >= n:return 0  # 递归边界:当 i 超过 source 的长度时,返回 0if memo[i] != -1:return memo[i]  # 如果当前索引的结果已经被计算过,则直接返回res = inf  # 初始化当前索引的最小成本为无穷大if source[i] == target[i]:res = dfs(i + 1)  # 如果当前字符相同,无需替换,直接计算下一个字符的成本p, q = trie.root, trie.root  # 初始化两个 Trie 指针,分别用于遍历 source 和 target# 尝试替换 source 中从索引 i 开始的所有可能的子串for j in range(i, n):index_p = ord(source[j]) - ord('a')  # 计算 source[j] 在 Trie 中的索引index_q = ord(target[j]) - ord('a')  # 计算 target[j] 在 Trie 中的索引p = p.children[index_p] if p else None  # 获取source[j] 在 Trie 中的节点q = q.children[index_q] if q else None  # 获取target[j] 在 Trie 中的节点if p is None or q is None:break  # 如果任一指针到达 Trie 的末端,表示前缀树中无法找到于source[i:j]或target[i:j]相匹配的字符串终止接下来的寻找if p.sid >= 0 and q.sid >= 0:# 如果当前子串在 original 和 changed 中都存在,则计算替换成本# 将当前子串替换成目标子串的成本加上剩余子串转换的成本res = min(res, shortest_paths[p.sid][q.sid] + dfs(j + 1))memo[i] = res  # 将结果存储在 memo 中,避免重复计算return resans = dfs(0)return -1 if ans >= inf else int(ans)# 示例使用
sol = Solution()
print(sol.minimumCost("abcd", "acbe", ["a", "b", "c", "c", "e", "d"], ["b", "c", "b", "e", "b", "e"],[2, 5, 5, 1, 2, 20]))

思考:如果只是为了获取每个字符串的唯一id为什么要用前缀树?

  • 处理大量可能重复的字符串时,Trie 树通过共享公共前缀,减少了重复处理相同前缀的字符串的需要。在如果一个字符串的前缀已经存在于 Trie 树中,那么代码只处理新的、独特的后缀部分。
  • Trie 树的查找和插入操作的时间复杂度大致为字符串长度的线性时间(O(m),其中 m 是字符串长度),这比在哈希表或平衡二叉搜索树中处理字符串(尤其是长字符串)要高效。
  • 虽然 Trie 树的空间复杂度可能比简单的哈希表高,但它通过共享公共前缀在处理大量相似字符串时变得更加空间高效。
  • Trie 树特别适合于需要快速检索和更新大量字符串的应用场景,如自动补全、拼写检查器、字符串排序等。

下面展示了不用前缀树的写法会超时: 

import heapq
from typing import Listclass Dijkstra:def __init__(self, size):self.size = sizeself.graph = [[] for _ in range(size)]def add_edge(self, u, v, weight):self.graph[u].append((v, weight))def shortest_path(self, start):distances = [float('inf')] * self.sizedistances[start] = 0pq = [(0, start)]while pq:current_distance, current_vertex = heapq.heappop(pq)if current_distance > distances[current_vertex]:continuefor neighbor, weight in self.graph[current_vertex]:distance = current_distance + weightif distance < distances[neighbor]:distances[neighbor] = distanceheapq.heappush(pq, (distance, neighbor))return distancesclass Solution:def minimumCost(self, source: str, target: str, original: List[str], changed: List[str], cost: List[int]) -> int:inf = 1e13string_to_id = {}id_counter = 0def get_id(s: str) -> int:nonlocal id_counterif s not in string_to_id:string_to_id[s] = id_counterid_counter += 1return string_to_id[s]m = len(original)dijkstra_solver = Dijkstra(m * 2)for i, c in enumerate(cost):x = get_id(original[i])y = get_id(changed[i])dijkstra_solver.add_edge(x, y, min(c, dijkstra_solver.graph[x][y] if y in dijkstra_solver.graph[x] else inf))shortest_paths = [dijkstra_solver.shortest_path(i) for i in range(id_counter)]n = len(source)memo = [-1] * ndef dfs(i):if i >= n:return 0if memo[i] != -1:return memo[i]res = infif source[i] == target[i]:res = dfs(i + 1)for j in range(i, n):src_substring = source[i:j+1]tgt_substring = target[i:j+1]if src_substring in string_to_id and tgt_substring in string_to_id:src_id = string_to_id[src_substring]tgt_id = string_to_id[tgt_substring]res = min(res, shortest_paths[src_id][tgt_id] + dfs(j + 1))memo[i] = resreturn resans = dfs(0)return -1 if ans >= inf else int(ans)

相关知识

LeetCode之团灭字典树相关题目-CSDN博客

LeetCode之团灭Dijkstra算法_leetcode 迪杰斯特拉-CSDN博客

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

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

相关文章

CentOS进入单用户模式

一、重启 二、出现内核选项 按“e” 三、编辑这一行 输入 rw init/sysroot/bin/sh 四、进入单用户模式 ctrlx 进入 五、切换目录 chroot /sysroot 六、然后你就操作你的系统了。 修改密码等等

【知识点随笔分享 | 第九篇】常见的限流算法

目录 前言&#xff1a; 1.固定窗口限流&#xff1a; 缺点&#xff1a; 2.滑动窗口限流&#xff1a; 优点&#xff1a; 滴桶限流&#xff1a; 缺点&#xff1a; 令牌桶限流&#xff1a; 优点&#xff1a; 总结: 前言&#xff1a; 当今互联网时代&#xff0c;随着网络…

IP编址,IP地址介绍与子网划分方法

网络层位于数据链路层与传输层之间。网络层中包含了许多协议&#xff0c;其中最为重要的协议就是IP协议。网络层提供了IP路由功能。理解IP路由除了要熟悉IP协议的工作机制之外&#xff0c;还必须理解IP编址以及如何合理地使用IP地址来设计网络。 上层协议类型 以太网帧中的Typ…

数据通信网络基础华为ICT网络赛道

目录 前言&#xff1a; 1.网络与通信 2.网络类型与网络拓扑 3.网络工程与网络工程师 前言&#xff1a; 数据通信网络基础是通信领域的基本概念&#xff0c;涉及数据传输、路由交换、网络安全等方面的知识。华为ICT网络赛道则是华为公司提出的一种技术路径&#xff0c;旨在通…

主机安全技术措施

目录 身份鉴别 进阶 访问控制 进阶 安全审计 进阶 ​编辑 剩余信息保护 入侵防范 进阶 恶意代码防范 资源控制 身份鉴别 进阶 访问控制 进阶 安全审计 进阶 剩余信息保护 入侵防范 进阶 恶意代码防范 资源控制 ~over~

Git 分布式版本控制系统(序章1)

第一章 Git 分布式版本控制系统 为什么学Git? 某些企业面试需要掌握Git&#xff0c;同时&#xff0c;也方便管理自己的Qt项目。 一、Git 客户端下载&#xff08;Windows&#xff09; 下载地址 https://gitee.com/all-about-git#git-%E5%A4%A7%E5%85%A8 二、Git 的特点 分支…

java的XWPFDocument3.17版本学习

maven依赖 <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>3.17</version> </dependency> 测试类&#xff1a; import org.apache.poi.openxml4j.exceptions.InvalidFormatExcep…

【MybatisPlus快速入门】(3)SpringBoot整合MybatisPlus 之 Lombok插件安装及MybatisPlus分页代码示例

目录 1.Lombok1.1 步骤1:添加lombok依赖 2.2 步骤2:安装Lombok的插件1.3 步骤3:模型类上添加注解2 分页功能2.1 步骤1:调用方法传入参数获取返回值2.2步骤2:设置分页拦截器2.3 步骤3:运行测试程序 之前我们已学习MyBatisPlus在代码示例与MyBatisPlus的简介&#xff0c;在这一节…

Web Components入门不完全指北

目前流行的各类前端框架&#xff0c;不管是react, angular还是vue&#xff0c;都有一个共同点&#xff0c;那就是支持组件化开发&#xff0c;但事实上随着浏览器的发展&#xff0c;现在浏览器也原生支持组件式开发&#xff0c;本文将通过介绍Web Components 的三个主要概念&…

vue场景 无分页列表条件过滤,子组件多选来自父组件的列表

日常开发中&#xff0c;经常会遇到下面场景&#xff1a; 页面加载一个无分页列表&#xff0c;同时工具栏设置多个条件可对列表过滤的场景(典型的就是关键字模糊查询)父组件传给子组件列表&#xff0c;子组件中需要多选列表多选&#xff0c;选择结果返回父组件 1 无分页列表过…

电子电器架构刷写方案——General Flash Bootloader

电子电器架构刷写方案——General Flash Bootloader 我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 注&#xff1a;文章1万字左右&#xff0c;深度思考者入&#xff01;&#xff01;&#xff01; 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免…

CentOS 7 Tomcat服务的安装

前提 安装ava https://blog.csdn.net/qq_36940806/article/details/134945175?spm1001.2014.3001.5501 1. 下载 wget https://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-9/v9.0.84/bin/apache-tomcat-9.0.84.tar.gzps: 可选择自己需要的版本下载安装https://mirr…

mysql原理--基于成本的优化

1.什么是成本 我们之前老说 MySQL 执行一个查询可以有不同的执行方案&#xff0c;它会选择其中成本最低&#xff0c;或者说代价最低的那种方案去真正的执行查询。不过我们之前对 成本 的描述是非常模糊的&#xff0c;其实在 MySQL 中一条查询语句的执行成本是由下边这两个方面组…

Kali Linux—借助 SET+MSF 进行网络钓鱼、生成木马、获主机shell、权限提升、远程监控、钓鱼邮件等完整渗透测试(一)

社会工程学—世界头号黑客凯文米特尼克在《欺骗的艺术》中曾提到&#xff0c;这是一种通过对受害者心理弱点、本能反应、好奇心、信任、贪婪等心理陷阱进行诸如欺骗、伤害等危害手段。 SET最常用的攻击方法有&#xff1a;用恶意附件对目标进行 E-mail 钓鱼攻击、Java Applet攻…

Unity之DOTweenPath轨迹移动

Unity之DOTweenPath轨迹移动 一、介绍 DOTweenPath二、操作说明1、Scene View Commands2、INfo3、Tween Options4、Path Tween Options5、Path Editor Options&#xff1a;轨迹编辑参数&#xff0c;就不介绍了6、ResetPath&#xff1a;重置轨迹7、Events&#xff1a;8、WayPoin…

Liteos移植_STM32_HAL库

0 开发环境 STM32CubeMX(HAL库)keil 5正点原子探索者STM32F4ZET6LiteOS-develop分支 1 STM32CubeMX创建工程 如果有自己的工程&#xff0c;直接从LiteOS源码获取开始 关于STM32CubeMX的安装&#xff0c;看我另一篇博客STM32CubeMX安装 工程配置 创建新工程 选择芯片【STM32F…

ElasticSearch入门介绍和实战

目录 1.ElasticSearch简介 1.1 ElasticSearch&#xff08;简称ES&#xff09; 1.2 ElasticSearch与Lucene的关系 1.3 哪些公司在使用Elasticsearch 1.4 ES vs Solr比较 1.4.1 ES vs Solr 检索速度 2. Lucene全文检索框架 2.1 什么是全文检索 2.2 分词原理之倒排索引…

JavaScript进阶(事件+获取元素+操作元素)

目录 事件基础 事件组成 执行事件的步骤 获取元素 根据ID获取元素 根据标签名获取元素 获取ol中的小li 类选择器&#xff08;html5新增的I9以上支持&#xff09; 获取body和html 操作元素 innerText和innerHtml 表单标签 样式属性操作 操作元素总结 事件基础 事…

设计模式--桥接模式

实验9&#xff1a;桥接模式 本次实验属于模仿型实验&#xff0c;通过本次实验学生将掌握以下内容&#xff1a; 1、理解桥接模式的动机&#xff0c;掌握该模式的结构&#xff1b; 2、能够利用桥接模式解决实际问题。 [实验任务]&#xff1a;两个维度的桥接模式 用桥接模式…

MySql的mvcc原理

目录 一、什么是mvcc? 二、什么是当前读,快照读? 当前读 快照读 三、mvcc实现原理 版本链 undo日志 Undo log 的用途 Read View(读视图) Read View几个属性 五、RR、RC级别下生成时机 一、什么是mvcc? mvcc全称Multi-Version Concurrency Control&#xff0c;即…