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

news2024/9/27 15:29:52

比赛详情

比赛地址

  • 题目一很简单
  • 题目二主要是题目长了点,其实解法很常规(比赛后才意识到)
  • 题目三套用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 先将他的元素添加到 arr
                if bob_num is not None:
                    arr.append(bob_num)
                # 然后 Alice 添加她的元素
                if alice_num is not None:
                    arr.append(alice_num)

            return arr

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

from typing import List

class Solution:
    def maximizeSquareArea(self, m: int, n: int, hFences: List[int], vFences: List[int]) -> int:
 
  
        def getGaps(fences, size):
            fences = sorted([1] + fences + [size])  #开始和结束的栏杆编号加入数组
            gaps = [fences[i+1] - fences[i] for i in range(len(fences) - 1)]  # 计算相邻栏杆的距离
            combinations = set()
            for i in range(len(gaps)):  # traverse the gaps starting from each and calculating all possible gap groups
                sum_ = 0
                for j in range(i, len(gaps)):  # traverse gaps combinations from the start and step by step sums up the widths
                    sum_ += gaps[j]
                    combinations.add(sum_)  # 把所有相邻的栏杆举例的可能组合计算出来

            return combinations

        mod = 10**9 + 7
        hGaps = getGaps(hFences, m)
        vGaps = getGaps(vFences, n)

        max_area = 0
        for gap in hGaps:
            if gap in vGaps:
                max_area = max(max_area, gap  ** 2)  # 计算面积

        return max_area % mod if max_area > 0 else -1

    
 

上面的解法有点绕,下面提供一个直观的写法。时间复杂度稍微高点也能Accept

mod = 10 ** 9 + 7
class Solution:
    def maximizeSquareArea(self, m: int, n: int, hFences: List[int], vFences: List[int]) -> int:
        a = [1] + hFences + [m]
        b = [1] + vFences + [n]
        tmp = set()
        for i in range(len(a)):
            for j in range(i):
                tmp.add(abs(a[i] - a[j]))
        ans = -1
        for i in range(len(b)):
            for j in range(i):
                if abs(b[i] - b[j]) in tmp:
                    ans = max(ans, (b[i] - b[j]) * (b[i] - b[j]))
        return -1 if ans == -1 else ans % mod

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

import heapq
from typing import List
from collections import defaultdict

class 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 0
            if (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:
                    continue
                visited.add(curr_node)
                if curr_node == tgt:
                    cost_cache[(src, tgt)] = curr_cost
                    return curr_cost
                for 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 = 0
        for s, t in zip(source, target):
            cost = dijkstra(s, t)
            if cost == float('inf'):
                return -1
            total_cost += cost

        return 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 = 0

    def put(self, s):
        # 将字符串 `s` 添加到 Trie 树中,并返回其唯一标识符
        # 如果字符串已存在,则返回其现有标识符
        node = self.root
        for 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.sid
            self.sid += 1
        return node.sid


class 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.size
        distances[start] = 0
        pq = [(0, start)]

        while pq:
            current_distance, current_vertex = heapq.heappop(pq)
            if current_distance > distances[current_vertex]:
                continue

            for neighbor, weight in self.graph[current_vertex]:
                distance = current_distance + weight
                if distance < distances[neighbor]:
                    distances[neighbor] = distance
                    heapq.heappush(pq, (distance, neighbor))

        return distances


class 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 = 1e13
        trie = 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 的长度时,返回 0

            if 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 res

        ans = 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 List


class Dijkstra:
    def __init__(self, size):
        self.size = size
        self.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.size
        distances[start] = 0
        pq = [(0, start)]

        while pq:
            current_distance, current_vertex = heapq.heappop(pq)
            if current_distance > distances[current_vertex]:
                continue

            for neighbor, weight in self.graph[current_vertex]:
                distance = current_distance + weight
                if distance < distances[neighbor]:
                    distances[neighbor] = distance
                    heapq.heappush(pq, (distance, neighbor))

        return distances


class Solution:
    def minimumCost(self, source: str, target: str, original: List[str], changed: List[str], cost: List[int]) -> int:
        inf = 1e13
        string_to_id = {}
        id_counter = 0

        def get_id(s: str) -> int:
            nonlocal id_counter
            if s not in string_to_id:
                string_to_id[s] = id_counter
                id_counter += 1
            return 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] * n

        def dfs(i):
            if i >= n:
                return 0

            if memo[i] != -1:
                return memo[i]

            res = inf
            if 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] = res
            return res

        ans = dfs(0)
        return -1 if ans >= inf else int(ans)

 

相关知识

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

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

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

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

相关文章

基于JAVA的考研专业课程管理系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 考研高校模块2.3 高校教师管理模块2.4 考研专业模块2.5 考研政策模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 考研高校表3.2.2 高校教师表3.2.3 考研专业表3.2.4 考研政策表 四、系统展示五、核…

程序员需要知道的职场真相

关于面试谈薪&#xff1a; 1. 你值多少钱&#xff0c;不是由老板决定的&#xff0c;也不是由你自己的能力决定&#xff0c;而是由市场决定的。这个技术就你一个会&#xff0c;你说多少钱就多少钱。这个技术 100W人会&#xff0c;不好意思&#xff0c;肯定是公司在一定的时间内&…

新零售模式:重新定义商业未来

随着科技的飞速发展&#xff0c;我们的生活方式正在经历着前所未有的变革。其中&#xff0c;新零售模式正逐渐成为商业领域的新热点&#xff0c;它正在重新定义我们的购物方式&#xff0c;并为企业带来更多的商业机会。 一、新零售模式概述 新零售模式是指将互联网、大数据、…

MySQL的替换函数及补全函数的使用

前提&#xff1a; mysql的版本是8.0以下的。不支持树形结构递归查询的。但是&#xff0c;又想实现树形结构的一种思路 提示&#xff1a;如果使用的是MySQL8.0及其以上的&#xff0c;想要实现树形结构&#xff0c;请参考&#xff1a;MySQL数据库中&#xff0c;如何实现递归查询…

《LIO-SAM阅读笔记》1.IMU预积分模块

前言&#xff1a; LIO-SAM是一个多传感器融合的紧耦合SLAM框架&#xff0c;融合的传感器类型有雷达、IMU和GPS&#xff0c;其中雷达和IMU在LIO-SAM框架中必须使用的。LIO-SAM的优化策略采用了GTSAM库&#xff0c;GTSAM库采用了因子图的优化方法&#xff0c;其提供了一些列C的外…

CRS-4995: The command ‘start resource’ is invalid in crsctl.

ntp时间调整后&#xff0c;节点1&#xff0c;advm 和acfs offline 处理办法&#xff1a; /u01/app/12.2.0.1/grid/bin/crsctl stop crs /u01/app/12.2.0.1/grid/bin/crsctl start crs 曾经尝试如下命令不起作用 /u01/app/12.2.0.1/grid/bin/acfsload start /u01/app/12.2…

2023_Spark_实验三十三:配置Standalone模式Spark3.4.2集群

实验目的&#xff1a;掌握Spark Standalone部署模式 实验方法&#xff1a;基于centos7部署Spark standalone模式集群 实验步骤&#xff1a; 一、下载spark软件 下载的时候下载与自己idea里对应版本的spark News | Apache Spark 选择任意一个下载即可 - spark 3.4.1 - spark …

Mybatis缓存机制详解与实例分析

前言&#xff1a; 本篇文章主要讲解Mybatis缓存机制的知识。该专栏比较适合刚入坑Java的小白以及准备秋招的大佬阅读。 如果文章有什么需要改进的地方欢迎大佬提出&#xff0c;对大佬有帮助希望可以支持下哦~ 小威在此先感谢各位小伙伴儿了&#x1f601; 以下正文开始 Mybat…

AI时代下,如何看待“算法利维坦”?程序员客栈程序员客栈​

ChatGPT的浪潮从2022年袭来后&#xff0c;至今热度不减&#xff0c;呈现出蓬勃发展的趋势。AI家居、医疗、教育、金融、公益、农业、艺术......AI真的已经走进了生活的方方面面&#xff0c;我们仿佛已经进入了AI时代&#xff0c;势不可挡。人工智能水平如此之高&#xff0c;不禁…

Dijkstra(迪杰斯特拉)算法总结

知识概览 Dijkstra算法适用于解决所有边权都是正数的最短路问题。Dijkstra算法分为朴素的Dijkstra算法和堆优化版的Dijkstra算法。朴素的Dijkstra算法时间复杂度为&#xff0c;适用于稠密图。堆优化版的Dijkstra算法时间复杂度为&#xff0c;适用于稀疏图。稠密图的边数m和是一…

ansible-playbook实操之一键搭建lnmp+wordpress

目录 1、架构和准备&#xff1a; 2、配置nginx角色&#xff1a; 3、配置mariadb角色&#xff1a; 4、配置php角色&#xff1a; 5、配置完之后&#xff0c;写脚本调用roles 6、配置完之后浏览器搭建wordpress&#xff1a; 1、架构和准备&#xff1a; 操控节点&#xff1a;…

2022第十二届PostgreSQL中国技术大会-核心PPT资料下载

一、峰会简介 本次大会以“突破•进化•共赢 —— 安全可靠&#xff0c;共建与机遇”为主题&#xff0c;助力中国数据库基础软件可掌控、可研究、可发展、可生产&#xff0c;并推动数据库生态的繁荣与发展。大会为数据库从业者、数据库相关企业、数据库行业及整个IT产业带来崭…

C# 类型和成员

C# 教程 - 类型及其成员 - C# | Microsoft Learnhttps://learn.microsoft.com/zh-cn/dotnet/csharp/tour-of-csharp/types 目录 类和对象 类型参数 基类 结构 接口 枚举 可为 null 的类型 元组 作为面向对象的语言&#xff0c;C# 支持封装、继承和多态性这些概念。 类可…

图像质量评估方法——结构相似性指数(SSIM)

结构相似性指数&#xff08;SSIM&#xff09;是一种全参考图像质量评估方法&#xff0c;用于比较两幅图像的相似性。 SSIM的计算涉及到亮度&#xff08;Luminance&#xff09;、对比度&#xff08;Contrast&#xff09;和结构&#xff08;Structure&#xff09;三个方面的相似性…

Goland配置leetcode

1. 安装 首先在goland的setting界面上找到Plugins&#xff0c;然后搜索关键字leetcode&#xff0c;找到LeetCode Editor&#xff0c;安装它。 在安装后&#xff0c;第一次需要对其进行配置&#xff0c;在Tools中找到LeetCode Plugins&#xff0c;如下图所示进行配置。首先国内…

Pytorch,16个超强转换函数全总结!!

哈喽&#xff0c;这些天无论是社群还是私信&#xff0c;很多人希望看到更多关于深度学习基础内容&#xff0c;这篇文章想要分享的是关于pytorch的转换函数。 建议大家一定要好好看看这部分&#xff0c;在平常的使用中&#xff0c;既多又重要&#xff01;&#xff01; 当然在 P…

正式官宣!谈思AutoSec 8周年年会暨中国汽车网络安全及数据安全合规峰会将于明年4月在沪召开

随着智能互联网时代的到来&#xff0c;智能汽车的安全形势变得更加严峻和复杂&#xff0c;网络资产的暴露和安全边界继续扩大。与传统的汽车车身安全问题相比&#xff0c;网络安全、数据安全、用户隐私等安全问题交织叠加&#xff0c;并加速了黑客对智能汽车领域的渗透&#xf…

在别人发来的文章上修改时,出现红色且带下划线的情况

这是因为一些比较严谨的机构将模板发过来在你修改的时候会出现特殊标记&#xff08;比如律师行业&#xff09; 这里想要直接在他的文档上进行修改&#xff0c;需要取消掉原来的修订配置 再次输入格式消失

我们一起动手学大模型应用开发

大模型正逐步成为信息世界的新革命力量&#xff0c;其通过强大的自然语言理解、自然语言生成能力&#xff0c;为开发者提供了新的、更强大的应用开发选择。 随着国内外井喷式的大模型 API 服务开放&#xff0c;如何基于大模型 API 快速、便捷地开发具备更强能力、集成大模型的…

three.js使用精灵模型Sprite渲染森林

效果&#xff1a; 源码&#xff1a; <template><div><el-container><el-main><div class"box-card-left"><div id"threejs" style"border: 1px solid red"></div><div class"box-right&quo…