leetcode 235周赛
竞赛链接:第 235 场周赛 - 力扣(LeetCode) (leetcode-cn.com)
题目一:
1816. 截断句子
题目描述及示例:
句子 是一个单词列表,列表中的单词之间用单个空格隔开,且不存在前导或尾随空格。每个单词仅由大小写英文字母组成(不含标点符号)。 例如,"Hello World"、"HELLO" 和 "hello world hello world" 都是句子。 给你一个句子 s 和一个整数 k ,请你将 s 截断 ,使截断后的句子仅含 前 k 个单词。返回 截断 s 后得到的句子。 示例 1: 输入:s = "Hello how are you Contestant", k = 4 输出:"Hello how are you" 解释: s 中的单词为 ["Hello", "how" "are", "you", "Contestant"] 前 4 个单词为 ["Hello", "how", "are", "you"] 因此,应当返回 "Hello how are you" 示例 2: 输入:s = "What is the solution to this problem", k = 4 输出:"What is the solution" 解释: s 中的单词为 ["What", "is" "the", "solution", "to", "this", "problem"] 前 4 个单词为 ["What", "is", "the", "solution"] 因此,应当返回 "What is the solution" 示例 3: 输入:s = "chopper is not a tanuki", k = 5 输出:"chopper is not a tanuki" 提示: 1 <= s.length <= 500 k 的取值范围是 [1, s 中单词的数目] s 仅由大小写英文字母和空格组成 s 中的单词之间由单个空格隔开 不存在前导或尾随空格
代码:
class Solution { public: string truncateSentence(string s, int k) { int n = 1; string ans; for(char x : s) { if(x == ' ') { if(n < k) n++; else break; } ans += x; } return ans; } };
题目二:
1817. 查找用户活跃分钟数
题目描述及示例:
给你用户在 LeetCode 的操作日志,和一个整数 k 。日志用一个二维整数数组 logs 表示,其中每个 logs[i] = [IDi, timei] 表示 ID 为 IDi 的用户在 timei 分钟时执行了某个操作。 多个用户 可以同时执行操作,单个用户可以在同一分钟内执行 多个操作 。 指定用户的 用户活跃分钟数(user active minutes,UAM) 定义为用户对 LeetCode 执行操作的 唯一分钟数 。 即使一分钟内执行多个操作,也只能按一分钟计数。 请你统计用户活跃分钟数的分布情况,统计结果是一个长度为 k 且 下标从 1 开始计数 的数组 answer ,对于每个 j(1 <= j <= k),answer[j] 表示 用户活跃分钟数 等于 j 的用户数。 返回上面描述的答案数组 answer 。 示例 1: 输入:logs = [[0,5],[1,2],[0,2],[0,5],[1,3]], k = 5 输出:[0,2,0,0,0] 解释: ID=0 的用户执行操作的分钟分别是:5 、2 和 5 。因此,该用户的用户活跃分钟数为 2(分钟 5 只计数一次) ID=1 的用户执行操作的分钟分别是:2 和 3 。因此,该用户的用户活跃分钟数为 2 2 个用户的用户活跃分钟数都是 2 ,answer[2] 为 2 ,其余 answer[j] 的值都是 0 示例 2: 输入:logs = [[1,1],[2,2],[2,3]], k = 4 输出:[1,1,0,0] 解释: ID=1 的用户仅在分钟 1 执行单个操作。因此,该用户的用户活跃分钟数为 1 ID=2 的用户执行操作的分钟分别是:2 和 3 。因此,该用户的用户活跃分钟数为 2 1 个用户的用户活跃分钟数是 1 ,1 个用户的用户活跃分钟数是 2 因此,answer[1] = 1 ,answer[2] = 1 ,其余的值都是 0 提示: 1 <= logs.length <= 104 0 <= IDi <= 109 1 <= timei <= 105 k 的取值范围是 [用户的最大用户活跃分钟数, 105]
题目分析:
用哈希表找人和分钟的对应关系使用集合记录活跃人数
代码:
class Solution { public: vector<int> findingUsersActiveMinutes(vector<vector<int>>& logs, int k) { unordered_map<int,unordered_set<int>> record; for(vector<int> us : logs){ record[us[0]].insert(us[1]); } vector<int> res(k,0); for(auto it = record.begin();it!=record.end();it++){ res[it->second.size()-1]++; } return res; } };
题目三:
1818. 绝对差值和
题目描述及示例:
给你两个正整数数组 nums1 和 nums2 ,数组的长度都是 n 。 数组 nums1 和 nums2 的 绝对差值和 定义为所有 |nums1[i] - nums2[i]|(0 <= i < n)的 总和(下标从 0 开始)。 你可以选用 nums1 中的 任意一个 元素来替换 nums1 中的 至多 一个元素,以 最小化 绝对差值和。 在替换数组 nums1 中最多一个元素 之后 ,返回最小绝对差值和。因为答案可能很大,所以需要对 109 + 7 取余 后返回。 |x| 定义为: 如果 x >= 0 ,值为 x ,或者 如果 x <= 0 ,值为 -x 示例 1: 输入:nums1 = [1,7,5], nums2 = [2,3,5] 输出:3 解释:有两种可能的最优方案: - 将第二个元素替换为第一个元素:[1,7,5] => [1,1,5] ,或者 - 将第二个元素替换为第三个元素:[1,7,5] => [1,5,5] 两种方案的绝对差值和都是 |1-2| + (|1-3| 或者 |5-3|) + |5-5| = 3 示例 2: 输入:nums1 = [2,4,6,8,10], nums2 = [2,4,6,8,10] 输出:0 解释:nums1 和 nums2 相等,所以不用替换元素。绝对差值和为 0 示例 3: 输入:nums1 = [1,10,4,4,2,7], nums2 = [9,3,5,1,7,4] 输出:20 解释:将第一个元素替换为第二个元素:[1,10,4,4,2,7] => [10,10,4,4,2,7] 绝对差值和为 |10-9| + |10-3| + |4-5| + |4-1| + |2-7| + |7-4| = 20 提示: n == nums1.length n == nums2.length 1 <= n <= 105 1 <= nums1[i], nums2[i] <= 105
题目分析:
使用 数列一 中的的一个数 替换 数列一中的一个数,只允许替换一次。要求|nums1[i] - nums2[i]|
(0 <= i < n
)的 总和(下标从 0 开始)最小。
首先替换数列一中最大的数不一定结果最小。
要找数列一对应数列二中的数,在数列一中最接近的数。而且要求其差值最大话,才能使结果最小。
可以使用set中内置的二分查找方法lower_bound(),寻找nums1中最接近的nums2[i]的数;
时间复杂度:O(nlogn)
代码:
class Solution { typedef long long ll; const int mod = 10e9 + 7; public: int minAbsoluteSumDiff(vector<int>& nums1, vector<int>& nums2) { set<ll>a; ll n = nums1.size(), max_n = 0; ll ans = 0; ll tem1, tem2; set<ll>::iterator flag1; for (int i = 0;i < n;i++) { ans = ans + abs(nums1[i] - nums2[i]); a.emplace(nums1[i]); } for (int i = 0;i < n;i++) { flag1 = a.lower_bound(nums2[i]); tem1 = abs(nums1[i] - nums2[i]); tem2 = abs(nums2[i] - *flag1); if (flag1 != a.end()) max_n = max(tem1 - tem2, max_n); if (flag1 != a.begin()) max_n = max(tem1 - abs(nums2[i] - *--flag1), max_n); } return (ans - max_n) %mod; } };
题目四:
1819. 序列中不同最大公约数的数目
题目描述及示例:
给你一个由正整数组成的数组 nums 。 数字序列的 最大公约数 定义为序列中所有整数的共有约数中的最大整数。 例如,序列 [4,6,16] 的最大公约数是 2 。 数组的一个 子序列 本质是一个序列,可以通过删除数组中的某些元素(或者不删除)得到。 例如,[2,5,10] 是 [1,2,1,2,4,1,5,10] 的一个子序列。 计算并返回 nums 的所有 非空 子序列中 不同 最大公约数的 数目 。 示例 1: 输入:nums = [6,10,3] 输出:5 解释:上图显示了所有的非空子序列与各自的最大公约数。 不同的最大公约数为 6 、10 、3 、2 和 1 。 示例 2: 输入:nums = [5,15,40,5,6] 输出:7 提示: 1 <= nums.length <= 105 1 <= nums[i] <= 2 * 105
题目分析:
首先需要找到数列中最大的数,应标记所有数,让结果ANS++(单个数的最大公因数一定是自己)
题目是要求所有子序列的最大公约数去重后的个数,观察数量级,肯定不能枚举所有子序列,因此我们可以从另一个角度出发,枚举所有可能的最大公约数,判断是否为某个子序列的公约数。
因此,我们要解决的问题就是如何判断一个数是否为某个子序列的公约数。
我们可以采用类似素数筛的方式,枚举每一个数i,判断 “数列中存在的” 2i、3i、4*i…..的公约数是否为i,如果为i,则贡献加一。
筛法的复杂度为n/1 + n/2 + n/3 + … + n/n,渐进为O(n * logn),而gcd的复杂度为O(logn),所以总复杂度为O(n * logn * logn)。
作者:simpleqin
代码:
#include <iostream> #include <string> #include <algorithm> #include <vector> #include <queue> #include <unordered_map> #include <set> using namespace std; int gcd(int a, int b)//a < b { while (a % b) b ^= a ^= b ^= a %= b; return b; } int main() { vector<int>nums = { 6, 10, 3 }; int max = 0; int ans = 0; bool temp[1000]; fill(temp, temp + 1000, false); for (int x : nums)//找出nums中的最大值并标记所有值 { if (max < x) max = x; if(temp[x] == false) { ans++; temp[x] = true; } } for (int i = 1;i <= max;i++) { if (temp[i]) continue; int n = 0; for (int j = i;j <= max;j+=i)//i和数组的数不断比较判断是否为最小公倍数 { if(temp[j]) { n = gcd(n, j); if (n == i) break; } } if (n == i) ans++; } cout << ans; }