第431场周赛:最长乘积等价子数组、计算字符串的镜像分数、收集连续 K 个袋子可以获得的最多硬币数量、不重叠区间的最大得分

news2025/1/7 17:53:28

Q1、最长乘积等价子数组

1、题目描述

给你一个由 正整数 组成的数组 nums

如果一个数组 arr 满足 prod(arr) == lcm(arr) * gcd(arr),则称其为 乘积等价数组 ,其中:

  • prod(arr) 表示 arr 中所有元素的乘积。
  • gcd(arr) 表示 arr 中所有元素的最大公因数 (GCD)。
  • lcm(arr) 表示 arr 中所有元素的最小公倍数 (LCM)。

返回数组 nums最长 乘积等价子数组 的长度。

子数组 是数组中连续的、非空的元素序列。

术语 gcd(a, b) 表示 ab最大公因数

术语 lcm(a, b) 表示 ab最小公倍数

2、解题思路

核心公式的推导
根据公式,我们需要检查:

prod(arr) = lcm(arr) × gcd(arr) \text{prod(arr)} = \text{lcm(arr)} \times \text{gcd(arr)} prod(arr)=lcm(arr)×gcd(arr)

其中:

  • prod(arr) \text{prod(arr)} prod(arr) 是子数组所有元素的乘积;
  • lcm(arr) \text{lcm(arr)} lcm(arr) 是子数组所有元素的最小公倍数,可以通过递归计算得到;
  • gcd(arr) \text{gcd(arr)} gcd(arr) 是子数组所有元素的最大公约数,也可以通过递归计算得到。

暴力枚举子数组
使用双层循环枚举所有可能的子数组的起点和终点,逐步计算子数组的:

  • prod(arr) \text{prod(arr)} prod(arr):通过逐一相乘得到;
  • lcm(arr) \text{lcm(arr)} lcm(arr):动态更新;
  • gcd(arr) \text{gcd(arr)} gcd(arr):动态更新。 对每个子数组,检查是否满足条件。

剪枝优化

  • 如果子数组的乘积 prod(arr) \text{prod(arr)} prod(arr) 超过合理范围(即 maxElement × overallLcm \text{maxElement}×\text{overallLcm} maxElement×overallLcm),直接提前终止当前循环;
  • 用整体数组的最小公倍数 overallLcm \text{overallLcm} overallLcm 来限制子数组可能的最大值,减少不必要的计算。

辅助函数

  • lcm(a, b):计算两个数的最小公倍数。
  • gcd(a, b):计算两个数的最大公约数。

3、代码实现

class Solution {
public:
    int maxLength(vector<int>& nums) {
        // 找到数组中的最大值, 用于限制子数组的最大可能 LCM 值
        int maxElement = *max_element(nums.begin(), nums.end());

        // 计算整个数组的最小公倍数, 用于减少无效计算
        int overallLcm = 1;
        for (int num : nums) {
            overallLcm = lcm(overallLcm, num);
        }

        int maxLength = 0; // 记录满足条件的最长子数组长度

        // 遍历每个子数组的起点
        for (int start = 0; start < nums.size(); ++start) {
            long long product = 1;    // 子数组元素的乘积
            long long currentLcm = 1; // 子数组的最小公倍数
            long long currentGcd = 0; // 子数组的最大公约数

            // 遍历从当前起点开始的子数组
            for (int end = start; end < nums.size(); ++end) {
                int currentElement = nums[end];

                // 更新子数组的乘积、LCM 和 GCD
                product *= currentElement;
                currentLcm = lcm(currentLcm, currentElement);
                currentGcd = gcd(currentGcd, currentElement);

                // 如果乘积等于 LCM * GCD, 则更新最大长度
                if (product == currentLcm * currentGcd) {
                    maxLength = max(maxLength, end - start + 1);
                }

                // 如果当前乘积超过合理范围 (避免无效计算), 提前终止循环
                if (product > overallLcm * maxElement) {
                    break;
                }
            }
        }

        return maxLength;
    }

private:
    // 计算两个数的 LCM(最小公倍数)
    long long lcm(long long a, long long b) { 
        return (a * b / gcd(a, b));
    }

    // 计算两个数的 GCD (最大公约数)
    long long gcd(long long a, long long b) {
        return b == 0 ? a : gcd(b, a % b);
    }
};

在这里插入图片描述

4、复杂度分析

时间复杂度

  • 外层循环:遍历所有可能的子数组起点 O(n);
  • 内层循环:遍历每个起点对应的终点,最坏情况下是 O(n);
  • 动态更新 LCM 和 GCD 的复杂度约为 O ( log ⁡ ( maxElement ) ) O(\log(\text{maxElement})) O(log(maxElement))
    综合复杂度为 O ( n 2 log ⁡ ( maxElement ) ) O(n^2 \log(\text{maxElement})) O(n2log(maxElement))

空间复杂度
使用常量辅助空间,因此空间复杂度为 O(1)。


Q2、计算字符串的镜像分数

1、题目描述

给你一个字符串 s

英文字母中每个字母的 镜像 定义为反转字母表之后对应位置上的字母。例如,'a' 的镜像是 'z''y' 的镜像是 'b'

最初,字符串 s 中的所有字符都 未标记

字符串 s 的初始分数为 0 ,你需要对其执行以下过程:

  • 从左到右遍历字符串。
  • 对于每个下标 i ,找到距离最近的 未标记 下标 j,下标 j 需要满足 j < is[j]s[i] 的镜像。然后 标记 下标 ij,总分加上 i - j 的值。
  • 如果对于下标 i,不存在满足条件的下标 j,则跳过该下标,继续处理下一个下标,不需要进行标记。

返回最终的总分。

2、解题思路

初始化数据结构:

  • 使用一个大小为 26 的向量 charStacks,其中每个元素是一个栈,表示英文字母从 'a''z' 的未标记位置索引。
  • 用变量 totalScore 记录最终分数。

遍历字符串:

  • 遍历字符串的每个字符,计算其索引和镜像索引。
  • 如果当前字符的镜像栈中有元素 (存在未标记的镜像字符索引),从镜像栈中弹出栈顶元素,计算得分并累加到 totalScore
  • 如果镜像栈为空,则将当前字符索引压入该字符的栈中。

返回总分:

  • 遍历完成后,totalScore 即为最终结果。

3、代码实现

class Solution {
public:
    long long calculateScore(string s) {
        // 使用26个栈, 分别对应 a-z 的字符
        vector<stack<int>> charStacks(26);

        long long totalScore = 0; // 记录总得分

        // 遍历字符串中的每个字符
        for (int index = 0; index < s.size(); ++index) {
            int currentChar = s[index] - 'a';         // 当前字符的索引 (0-25)
            int complementaryChar = 25 - currentChar; // 互补字符的索引 (0-25)

            // 如果互补字符的栈不为空, 则可以匹配, 计算得分
            if (!charStacks[complementaryChar].empty()) {
                // 计算当前得分: 当前索引减去互补字符的最近索引
                totalScore += index - charStacks[complementaryChar].top();
                // 弹出互补字符的栈顶元素 (标记为已使用)
                charStacks[complementaryChar].pop();
            } else {
                // 否则, 将当前字符的索引压入对应栈
                charStacks[currentChar].push(index);
            }
        }

        return totalScore; // 返回总得分
    }
};

在这里插入图片描述

4、复杂度分析

时间复杂度
每个字符在遍历过程中只会被压栈和弹栈一次,时间复杂度为 O(n)。

空间复杂度
需要额外的 26 个栈,每个栈的大小总和不超过 n,空间复杂度为 O(n)。


Q3、收集连续 K 个袋子可以获得的最多硬币数量

1、题目描述

在一条数轴上有无限多个袋子,每个坐标对应一个袋子。其中一些袋子里装有硬币。

给你一个二维数组 coins,其中 coins[i] = [li, ri, ci] 表示从坐标 liri 的每个袋子中都有 ci 枚硬币。

数组 coins 中的区间互不重叠。

另给你一个整数 k

返回通过收集连续 k 个袋子可以获得的 最多 硬币数量。

2、解题思路

滑动窗口法

由于地毯覆盖的是连续的袋子,因此我们可以用 滑动窗口 来计算当前地毯位置下的硬币总和,并动态调整窗口以获取最大硬币数。

  • 使用窗口 [left, right] 表示当前地毯覆盖的范围。
  • 窗口右边界扩展:每次将当前区间的硬币加入到总和中。
  • 窗口左边界收缩:当地毯的覆盖范围超过 k 时,移除左边部分的硬币。

双向覆盖

为了考虑不同覆盖方向(从左到右,从右到左)的影响,算法需要:

  1. 按照区间起点升序排序,计算从左到右的最大覆盖硬币数。
  2. 将区间起点和终点取反后再次排序,计算从右到左的最大覆盖硬币数。

最终结果为两种覆盖方向的最大值。

3、代码实现

class Solution {
public:
    long long maximumCoins(vector<vector<int>>& coins, int carpetLen) {
        // 按起点升序排序区间, 方便从左到右计算
        sort(coins.begin(), coins.end());

        // 计算从左到右的最大硬币覆盖数量
        long long maxCoins = calculateMaxCoins(coins, carpetLen);

        // 反转每个区间的起点和终点
        for (auto& tile : coins) {
            int temp = tile[0];
            tile[0] = -tile[1];         // 起点变为负的终点
            tile[1] = -temp;            // 终点变为负的起点
        }

        // 按新起点升序排序区间, 方便从右到左计算
        sort(coins.begin(), coins.end());

        // 计算从右到左的最大硬币覆盖数量, 并更新最终结果
        maxCoins = max(maxCoins, calculateMaxCoins(coins, carpetLen));
        return maxCoins;
    }

private:
    // 计算滑动窗口内的最大硬币覆盖数量
    long long calculateMaxCoins(const vector<vector<int>>& tiles, int carpetLen) {
        long long maxCovered = 0;       // 记录最大覆盖硬币数
        long long currentCovered = 0;   // 当前窗口的硬币总数
        int left = 0;                   // 左指针, 表示窗口的起始位置

        // 遍历每个区间, 调整滑动窗口
        for (int right = 0; right < tiles.size(); ++right) {
            int start = tiles[right][0]; // 当前区间的起始位置
            int end = tiles[right][1];   // 当前区间的终点位置
            int coins = tiles[right][2]; // 当前区间的硬币数

            // 增加当前区间的硬币到窗口总和
            currentCovered += static_cast<long long>(end - start + 1) * coins;

            // 如果当前窗口超出地毯长度, 收缩左指针
            while (tiles[left][1] + carpetLen - 1 < end) {
                int leftStart = tiles[left][0];
                int leftEnd = tiles[left][1];
                int leftCoins = tiles[left][2];
                // 减去左指针对应的硬币数
                currentCovered -= static_cast<long long>(leftEnd - leftStart + 1) * leftCoins;
                ++left; // 左指针右移
            }

            // 计算当前窗口的未完全覆盖部分, 并更新最大覆盖硬币数
            long long uncovered = max(static_cast<long long>(end - carpetLen + 1 - tiles[left][0]) * tiles[left][2], 0LL);
            maxCovered = max(maxCovered, currentCovered - uncovered);
        }

        return maxCovered; // 返回当前方向的最大硬币覆盖数
    }
};

在这里插入图片描述

4、复杂度分析

时间复杂度

  • 排序:两次排序,时间复杂度为 O ( n log ⁡ n ) O(n \log n) O(nlogn),其中 n 是区间数量。
  • 滑动窗口:遍历每个区间的右边界,时间复杂度为 O ( n ) O(n) O(n)
  • 总时间复杂度为 O ( n log ⁡ n ) O(n \log n) O(nlogn)

空间复杂度

  • 使用了辅助变量和少量指针,空间复杂度为 O ( 1 ) O(1) O(1)

Q4、不重叠区间的最大得分

1、题目描述

给你一个二维整数数组 intervals,其中 intervals[i] = [li, ri, weighti]。区间 i 的起点为 li,终点为 ri,权重为 weighti。你最多可以选择 4 个互不重叠 的区间。所选择区间的 得分 定义为这些区间权重的总和。

返回一个至多包含 4 个下标且字典序最小的数组,表示从 intervals 中选中的互不重叠且得分最大的区间。

如果两个区间没有任何重叠点,则称二者 互不重叠 。特别地,如果两个区间共享左边界或右边界,也认为二者重叠。

数组 a 的字典序小于数组 b 的前提是:当在第一个不同的位置上,a 的元素小于 b 的对应元素。如果前 min(a.length, b.length) 个元素均相同,则较短的数组字典序更小。

2、解题思路

  1. 预处理与排序:

    • 将输入数据转换为结构体数组以便操作。

    • 按区间的 右端点升序 排序,方便后续计算前一个不重叠区间。

  2. 动态规划:

    • 定义 dp[i][j] 表示前 i 个区间中选择 j 个的最大权重及对应区间索引。

    • 对于每个区间,既可以选择,也可以不选择;需要比较两种情况:

      • 不选择:直接继承前一个状态。
      • 选择:通过二分找到最后一个不与当前区间重叠的区间,加上当前权重。
      • 若两种选择的权重相等,则选择字典序更小的方案。
  3. 回溯最优解:

    • 最终结果是 dp[n][4],即前 n 个区间中选择最多 4 个的最优解。

3、代码实现

class Solution {
private:
    struct Interval {
        int start;  // 区间起点
        int end;    // 区间终点
        int weight; // 区间权重
        int index;  // 区间索引
    };

public:
    vector<int> maximumWeight(vector<vector<int>>& intervals) {
        int n = intervals.size();

        // 1. 将输入的二维数组转换为结构体数组, 便于操作
        vector<Interval> sortedIntervals(n);
        for (int i = 0; i < n; ++i) {
            sortedIntervals[i] = {intervals[i][0], intervals[i][1], intervals[i][2], i};
        }

        // 2. 按区间的右端点升序排序
        sort(sortedIntervals.begin(), sortedIntervals.end(),
             [](const Interval& a, const Interval& b) {
                 return a.end < b.end;
             });

        // 3. 动态规划表, dp[i][j] 表示前 i 个区间选择 j 个的最大权重及对应区间索引
        vector<array<pair<long long, vector<int>>, 5>> dp(n + 1);

        for (int i = 0; i < n; ++i) {
            const auto& current = sortedIntervals[i];

            // 找到当前区间左端点之前结束的最近区间索引
            int previous = lower_bound(sortedIntervals.begin(), sortedIntervals.begin() + i, current.start,
                                        [](const Interval& interval, int value) {
                                            return interval.end < value;
                                        }) - sortedIntervals.begin();

            for (int j = 1; j < 5; ++j) {
                // 不选当前区间的情况
                long long excludeWeight = dp[i][j].first;

                // 选当前区间的情况
                long long includeWeight = dp[previous][j - 1].first + current.weight;

                // 如果选择当前区间更优, 则更新 DP 表
                if (excludeWeight < includeWeight) {
                    vector<int> newIndices = dp[previous][j - 1].second;
                    newIndices.push_back(current.index);
                    sort(newIndices.begin(), newIndices.end()); // 保持字典序

                    // 更新权重和区间索引
                    dp[i + 1][j] = {includeWeight, newIndices};
                } else if (excludeWeight > includeWeight) {
                    // 如果不选当前区间更优,则继承上一步的状态
                    dp[i + 1][j] = dp[i][j];
                } else {
                    // 如果两种选择权重相等, 选择字典序更小的方案
                    vector<int> newIndices = dp[previous][j - 1].second;
                    newIndices.push_back(current.index);
                    sort(newIndices.begin(), newIndices.end()); // 保持字典序

                    if (dp[i][j].second < newIndices) {
                        dp[i + 1][j] = dp[i][j];
                    } else {
                        dp[i + 1][j] = {includeWeight, newIndices};
                    }
                }
            }
        }

        // 4. 返回选择 4 个区间时的区间索引
        return dp[n][4].second;
    }
};

在这里插入图片描述

4、时间复杂度分析

  1. 排序: O ( n log ⁡ n ) O(n \log n) O(nlogn)
  2. 动态规划: O ( n × 4 × log ⁡ n ) O(n \times 4 \times \log n) O(n×4×logn),二分查找的复杂度为 O ( log ⁡ n ) O(\log n) O(logn)
  3. 总复杂度: O ( n log ⁡ n ) O(n \log n) O(nlogn)


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

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

相关文章

【微服务】2、网关

Spring Cloud微服务网关技术介绍 单体项目拆分微服务后的问题 服务地址问题&#xff1a;单体项目端口固定&#xff08;如黑马商城为8080&#xff09;&#xff0c;拆分微服务后端口各异&#xff08;如购物车808、商品8081、支付8086等&#xff09;且可能变化&#xff0c;前端难…

使用JMeter玩转tidb压测

作者&#xff1a; du拉松 原文来源&#xff1a; https://tidb.net/blog/3f1ada39 一、前言 tidb是mysql协议的&#xff0c;所以在使用过程中使用tidb的相关工具连接即可。因为jmeter是java开发的相关工具&#xff0c;直接使用mysql的jdbc驱动包即可。 二、linux下安装jmet…

2024网络安全运营方案概述(附实践资料合集)

以下是网络安全运营方案的详细内容&#xff1a; 一、目标与原则 目标&#xff1a;建立一套安全高效、灵活性强的网络安全运营体系&#xff0c;实现对网络安全的全面监控、防护和应急响应。原则&#xff1a; 全员参与&#xff1a;网络安全是全员共同的责任&#xff0c;所有员工…

使用Python进行图像裁剪和直方图分析

一、简介 在数字图像处理领域&#xff0c;裁剪和分析图像的直方图是两个非常基本且重要的操作。本文将通过一个简单的Python项目&#xff0c;展示如何使用skimage和matplotlib库来裁剪图像并分析其RGB通道的直方图。 二、环境准备 在开始之前&#xff0c;请确保你已经安装了以…

vue3-dom-diff算法

vue3diff算法 什么是vue3diff算法 Vue3中的diff算法是一种用于比较虚拟DOM树之间差异的算法&#xff0c;其目的是为了高效地更新真实DOM&#xff0c;减少不必要的重渲染 主要过程 整个过程主要分为以下五步 前置预处理后置预处理仅处理新增仅处理后置处理包含新增、卸载、…

【U8+】用友U8软件中,出入库流水输出excel的时候提示报表输出引擎错误。

【问题现象】 通过天联高级版客户端登录拥有U8后&#xff0c; 将出入库流水输出excel的时候&#xff0c;提示报表输出引擎错误。 进行报表输出时出现错误&#xff0c;错误信息&#xff1a;找不到“fd6eea8b-fb40-4ce4-8ab4-cddbd9462981.htm”。 如果您正试图从最近使用的文件列…

[SMARTFORMS] 创建样式模板

通过事务码SMARTFORMS创建样式模板 选择样式&#xff0c;自定义样式模板名称ZST_DEMO_2025 点击"创建"按钮&#xff0c;跳转至样式模板详情页面&#xff0c;我们可以在该页面上设置SMARTFORMS表单相关的样式 在段落样式处&#xff0c;右键选择创建节点&#xff0c;输…

基于51单片机和DS3231时钟模块、LCD1602(I2C通信)模块的可调时钟+温度测量+计时+闹钟

目录 系列文章目录前言一、效果展示二、原理分析三、各模块代码1、延时函数2、定时器03、定时器14、独立按键5、DS3231时钟模块6、LCD1602模块&#xff08;PCF8574T驱动&#xff09; 四、主函数总结 系列文章目录 前言 之前做过一个类似的&#xff0c;用到了很多外设&#xff…

通义视觉推理大模型QVQ-72B-preview重磅上线

Qwen团队推出了新成员QVQ-72B-preview&#xff0c;这是一个专注于提升视觉推理能力的实验性研究模型。提升了视觉表示的效率和准确性。它在多模态评测集如MMMU、MathVista和MathVision上表现出色&#xff0c;尤其在数学推理任务中取得了显著进步。尽管如此&#xff0c;该模型仍…

企业级Nosql数据库和Redis集群

一、关系数据库和Nosql数据库 关系数据库 定义&#xff1a;关系数据库是建立在关系模型基础上的数据库。它使用表格&#xff08;关系&#xff09;来存储数据&#xff0c;通过行和列的形式组织信息。例如&#xff0c;一个简单的学生信息表可能有 “学号”“姓名”“年龄”“班级…

Ant Design中Flex布局、Grid布局和Layout布局详解

好的&#xff0c;我们来更详细地探讨 Ant Design 中的 Flex布局、Grid布局 和 Layout布局 的特点、用法、适用场景&#xff0c;以及如何灵活运用它们来构建页面。下面将从各个方面进行更深入的分析&#xff0c;并提供具体的实例。 VueFlex布局实现响应式布局 1. Flex布局 概念…

基于FPGA的SNN脉冲神经网络之IM神经元verilog实现,包含testbench

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 2.算法运行软件版本 vivado2019.2 3.部分核心程序 &#xff08;完整版代码包含详细中文注释和操作步骤视频&#xff0…

健身房管理系统多身份

本文结尾处获取源码。 本文结尾处获取源码。 本文结尾处获取源码。 一、相关技术 后端&#xff1a;Java、JavaWeb / Springboot。前端&#xff1a;Vue、HTML / CSS / Javascript 等。数据库&#xff1a;MySQL 二、相关软件&#xff08;列出的软件其一均可运行&#xff09; I…

三甲医院等级评审八维数据分析应用(四)--数据质量管理篇

一、引言 1.1 研究背景与意义 在医疗卫生领域,医院评审是衡量医院综合实力、保障医疗服务质量的重要手段。其中,三甲评审作为我国医院评审体系中的最高级别,对医院的管理、医疗技术、服务质量等各方面都设定了严格标准。医务科作为医院医疗质量管理的核心部门,肩负着协调…

Solidity合约编写(一)

Solidity IDE地址&#xff1a;Remix - Ethereum IDE 点击进入后在contract文件夹下创建合约 合约代码如下&#xff1a; // SPDX-License-Identifier: MIT pragma solidity ^0.8.26;contract SimpleStorage{bool hasFavorNumtrue;uint256 favorNum5;string favorNums"fiv…

嵌入式系统(将软件嵌入到硬件里面)

目录 Linux起源 查看操作系统的版本 查看内核的版本&#xff1a; 内核系统架构 系统关机或重启命令 关机&#xff1a; 重启&#xff1a; linux下的软件安装 两种软件包管理机制&#xff1a; deb软件包分为两种&#xff1a; 软件包的管理工具&#xff1a;dpkg apt 1…

会员制电商创新:开源 AI 智能名片与 2+1 链动模式的协同赋能

摘要&#xff1a;本文聚焦于电商领域会员制的关键作用&#xff0c;深入探讨在传统交易模式向数字化转型过程中&#xff0c;如何借助开源 AI 智能名片以及 21 链动模式商城小程序&#xff0c;实现对会员数据的精准挖掘与高效利用&#xff0c;进而提升企业的营销效能与客户洞察能…

Day29 - 大模型RAG,检索增强生成

如何调用第三方大模型 阿里大模型 1. 获取百炼平台 api key 【阿里云】-【产品】-【人工智能与机器学习】-【大模型服务平台百炼】 2. 参考LangChain文档 https://python.langchain.com/docs/introduction/ 3. 连接阿里大模型 """1. 引入模型 "&quo…

谷粒商城项目125-spring整合high-level-client

新年快乐! 致2025年还在努力学习的你! 你已经很努力了&#xff0c;今晚就让自己好好休息一晚吧! 在后端中选用哪种elasticsearch客户端&#xff1f; elasticsearch可以通过9200或者9300端口进行操作 1&#xff09;9300&#xff1a;TCP spring-data-elasticsearch:transport-…

c语言的文件操作与文件缓冲区

目录 C语言文件操作函数汇总 简单介绍文件 为什么使用文件 什么是文件 文件名 二进制文件和文本文件 流和标准流 流 标准流 文件指针 文件的打开和关闭 文件的顺序读写 顺序读写函数介绍 文件的随机读写 fseek ftell rewind 文件读取结束的判定 文件缓冲区 缓…