Leetcode 第 398 场周赛题解

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,可以快速判断子数组和是否为 0
        vector<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 的方案数。

枚举当前使用哪个操作:

  1. 使用操作一:前提是 i != 0 && preDown == false。使用操作一后,要解决的子问题是 dfs(i - 1, jump, true),加入返回值中。
  2. 使用操作二:要解决的子问题是 dfs(i + (1 << jump), jump + 1, false),加入返回值中。
  3. 此外,如果 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)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UestcXiye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值