Leetcode 第 398 场周赛题解
- Leetcode 第 398 场周赛题解
- 题目1:3151. 特殊数组 I
- 思路
- 代码
- 复杂度分析
- 题目2:3152. 特殊数组 II
- 思路
- 代码
- 复杂度分析
- 题目3:3153. 所有数对中数位不同之和
- 思路
- 代码
- 复杂度分析
- 题目4:3154. 到达第 K 级台阶的方案数
- 思路
- 代码
- 复杂度分析
Leetcode 第 398 场周赛题解
题目1:3151. 特殊数组 I
思路
遍历数组 nums,依次比较相邻元素是否奇偶性不同即可。
代码
class Solution {
public:bool isArraySpecial(vector<int>& nums) {if (nums.size() == 1)return true;int n = nums.size();for (int i = 0; i < n - 1; i++)if (!different(nums[i], nums[i + 1]))return false;return true;}bool different(int a, int b) { return abs(a - b) % 2; }
};
复杂度分析
时间复杂度:O(n),其中 n 是数组 nums 的长度。
空间复杂度:O(1)。
题目2:3152. 特殊数组 II
思路
预处理 + 前缀和。
定义一个长为 n−1 的数组 a,其中 a[i] = nums[i] % 2 == nums[i+1] % 2,那么 a 的从 to-1 到 from 的子数组和等于 0,就说明询问的子数组是特殊数组。
计算 a 的前缀和 preSum,可以快速判断子数组和是否为 0。
代码
/** @lc app=leetcode.cn id=3152 lang=cpp** [3152] 特殊数组 II*/// @lc code=start
class Solution
{
public:vector<bool> isArraySpecial(vector<int> &nums, vector<vector<int>> &queries){int n = nums.size();// 定义一个长为 n−1 的数组 a,其中 a[i] = nums[i] % 2 == nums[i+1] % 2// 那么 a 的从 to-1 到 from 的子数组和等于 0,就说明询问的子数组是特殊数组。// 计算 a 的前缀和 preSum,可以快速判断子数组和是否为 0vector<int> preSum(n, 0);for (int i = 1; i < n; i++){// 相邻两数的异或和的最低位取反int a = !((nums[i] ^ nums[i - 1]) & 1);preSum[i] = preSum[i - 1] + a;}int m = queries.size();vector<bool> ans(m);for (int i = 0; i < m; i++){int from = queries[i][0], to = queries[i][1];ans[i] = preSum[to] - preSum[from] == 0;}return ans;}
};
// @lc code=end
复杂度分析
时间复杂度:O(n+m),其中 n 是数组 nums 的长度,m 是数组 queries 的长度。
空间复杂度:O(n),其中 n 是数组 nums 的长度。
题目3:3153. 所有数对中数位不同之和
思路
遍历数组 nums, 对每一位的数字进行计数并保存在数组 cnt 中。
遍历完数组得到 cnt 后,cnt[i][j] 表示在第 i 位,数字为 j 的数的个数,那么在第 i 位,数字不为 j 的数的个数为 n−cnt[i][j],其中 n 为数组 nums 的长度。根据乘法原理可以知道,在第 i 位,一共有 cnt[i][j]∗(n−cnt[i][j])/2 对数在该位的数字不相同。累加每一位数字不相同的数对数量,即可得到所需要的答案。
代码
/** @lc app=leetcode.cn id=3153 lang=cpp** [3153] 所有数对中数位不同之和*/// @lc code=start
class Solution
{
public:long long sumDigitDifferences(vector<int> &nums){int n = nums.size();int cnt[9][10];memset(cnt, 0, sizeof(cnt));for (int num : nums){int i = 0;while (num){cnt[i][num % 10]++;i++;num /= 10;}}long long ans = 0LL;for (int i = 0; i < 9; i++)for (int j = 0; j < 10; j++)ans += cnt[i][j] * (n - cnt[i][j]);return ans >> 1;}
};
// @lc code=end
复杂度分析
时间复杂度:O(l*n+l*d),其中 n 为数组 nums 的长度,l 为数组 nums 中数据的十进制位数,l 的最大值为 9,d 为从 0 到 9 共 10 个数字。
空间复杂度:O(l*d),其中 l 为数组 nums 中数据的十进制位数,l 的最大值为 9,d 为从 0 到 9 共 10 个数字。
题目4:3154. 到达第 K 级台阶的方案数
思路
记忆化搜索。
定义 dfs(int i, int jump, bool preDown) 表示当前位于台阶 i,已经使用了 jump 次第二种操作,preDown:上一次操作是否为操作一时,到达台阶 k 的方案数。
枚举当前使用哪个操作:
- 使用操作一:前提是 i != 0 && preDown == false。使用操作一后,要解决的子问题是 dfs(i - 1, jump, true),加入返回值中。
- 使用操作二:要解决的子问题是 dfs(i + (1 << jump), jump + 1, false),加入返回值中。
- 此外,如果 i=k,可以把返回值加一。
递归边界:如果 i>k+1,由于操作一不能连续使用,无法到达 k,返回 0。
递归入口:dfs(1,0,false),即答案。
代码
/** @lc app=leetcode.cn id=3154 lang=cpp** [3154] 到达第 K 级台阶的方案数*/// @lc code=start
class Solution
{
private:unordered_map<long long, int> memo;public:int waysToReachStair(int k){// 当前位于台阶 i,已经使用了 jump 次第二种操作,preDown:上一次操作是否为操作一function<int(int, int, bool)> dfs = [&](int i, int jump, bool preDown) -> int{if (i > k + 1)return 0;long long state = (long long)i << 32 | jump << 1 | preDown;if (memo.contains(state))return memo[state];int res = 0;if (i == k)res++;if (i != 0 && preDown == false){ // 操作一res += dfs(i - 1, jump, true);}res += dfs(i + (1 << jump), jump + 1, false); // 操作二memo[state] = res; // 记忆化return res;};// 从台阶 1 开始,一开始 jump 为 0,前一次操作不为操作二return dfs(1, 0, false);}
};
// @lc code=end
复杂度分析
时间复杂度:O(log2k)。
空间复杂度:O(log2k)。