leetcode每日一题 2025年4月、5月

4月26日 leetcode 2444. 统计定界子数组的数目(难)

防老年痴呆计划第一天就来道困难题,直接给我带走了,幸好读书的时候掌握了翻答案的技能。

【题目描述】

给你一个整数数组 nums 和两个整数 minK 以及 maxK

nums 的定界子数组是满足下述条件的一个子数组:

  • 子数组中的 最小值 等于 minK
  • 子数组中的 最大值 等于 maxK

返回定界子数组的数目。

子数组是数组中的一个连续部分。

示例 1:

输入:nums = [1,3,5,2,7,5], minK = 1, maxK = 5
输出:2
解释:定界子数组是 [1,3,5] 和 [1,3,5,2] 。

示例 2:

输入:nums = [1,1,1,1], minK = 1, maxK = 1
输出:10
解释:nums 的每个子数组都是一个定界子数组。共有 10 个子数组。

题目地址:https://leetcode.cn/problems/count-subarrays-with-fixed-bounds/description/?envType=daily-question&envId=2025-04-26

【解答】

使用left作为左边界(不包含在子数组内),然后从left+1位置开始向右查找,当遇到等于minKmaxK的数字,将其位置记为minIdxmaxIdx,当minIdxmaxIdx都有合法值时,代表以right为右边界的子数组有满足条件的结果,满足条件的子数组数为Math.min(minIdx, maxIdx) - left。在向右查找的过程中,如果遇到了小于minK或者大于maxK的元素,代表以right为右边界的子数组已无法满足条件,令left=right,继续从left+1位置开始查找。

    public long countSubarrays(int[] nums, int minK, int maxK) {
        long res = 0;
        int left = -1, minIdx = -1, maxIdx = -1;
        for (int right = 0; right < nums.length; right++) {
            // nums[i]无法满足子数组中数字要求,重新设定左边界
            if (nums[right] < minK || nums[right] > maxK) {
                minIdx = -1;
                maxIdx = -1;
                left = right;
            }
            if (nums[right] == minK) {
                minIdx = right;
            }
            if (nums[right] == maxK) {
                maxIdx = right;
            }
            if (minIdx != -1 && maxIdx != -1) {
                res += Math.min(minIdx, maxIdx) - left;
            }
        }
        return res;
    }

【参考文献】

[1]. 力扣官方题解 https://leetcode.cn/problems/count-subarrays-with-fixed-bounds/solutions/3651609/tong-ji-ding-jie-zi-shu-zu-de-shu-mu-by-q60k7/?envType=daily-question&envId=2025-04-26

4月27日 leetcode 3392. 统计符合条件长度为 3 的子数组数目(简)

这题就顺眼多了,\o/\o/\o/

【题目描述】
给你一个整数数组 nums ,请你返回长度为 3 的 子数组 的数量,满足第一个数和第三个数的和恰好为第二个数的一半。

子数组 指的是一个数组中连续 非空 的元素序列。

示例:

输入:nums = [1,2,1,4,1]

输出:1

解释:

只有子数组 [1,4,1] 包含 3 个元素且第一个和第三个数字之和是中间数字的一半。

题目地址:https://leetcode.cn/problems/count-subarrays-of-length-three-with-a-condition/?envType=daily-question&envId=2025-04-27

【解答】

public int countSubarrays(int[] nums) {
    int sum = 0;
    int num1, num2, num3;
    for (int i = 2; i < nums.length; i++) {
        num1 = nums[i - 2];
        num2 = nums[i - 1];
        num3 = nums[i];
        if ((num1 + num3) * 2 == num2) sum++;
    }
    return sum;
}

4月28日 leetcode 2302. 统计得分小于 K 的子数组数目(难)

做了26号那道题,这道题一下子就有思路了,感觉这两道题其实算不上困难题。

【题目描述】

一个数组的 分数 定义为数组之和 乘以 数组的长度。

  • 比方说,[1, 2, 3, 4, 5] 的分数为 (1 + 2 + 3 + 4 + 5) * 5 = 75

给你一个正整数数组 nums 和一个整数 k ,请你返回 nums 中分数 严格小于 k非空整数子数组数目

子数组 是数组中的一个连续元素序列。

示例 1:

输入:nums = [2,1,4,3,5], k = 10
输出:6
解释:
有 6 个子数组的分数小于 10 :

  • [2] 分数为 2 * 1 = 2 。
  • [1] 分数为 1 * 1 = 1 。
  • [4] 分数为 4 * 1 = 4 。
  • [3] 分数为 3 * 1 = 3 。
  • [5] 分数为 5 * 1 = 5 。
  • [2,1] 分数为 (2 + 1) * 2 = 6 。
    注意,子数组 [1,4] 和 [4,3,5] 不符合要求,因为它们的分数分别为 10 和 36,但我们要求子数组的分数严格小于 10 。

题目地址:https://leetcode.cn/problems/count-subarrays-with-score-less-than-k/?envType=daily-question&envId=2025-04-28

【滑动窗口】

r每向右滑动一步,就判断子数组[l, r]是否满足条件,如果满足,结果就加上该子数组中包含多少个以r为右边界的子数组;如果不满足,就让左边界l向右移动到满足条件为止。

public long countSubarrays(int[] nums, long k) {
    long res = 0, sum = 0;
    int l = 0, r = 0;
    while (r < nums.length) {
        sum += nums[r];
        while (l <= r && sum * (r - l + 1) >= k) {
            sum -= nums[l++];
        }
        // +以r为右边界的符合条件的子数组的个数
        res += r - l + 1;
        r++;
    }
    return res;
}

4月29日 leetcode 2962. 统计最大元素出现至少 K 次的子数组(中)

【题目描述】

给你一个整数数组 nums 和一个 正整数 k

请你统计有多少满足 「 nums 中的 最大 元素」至少出现 k 次的子数组,并返回满足这一条件的子数组的数目。

子数组是数组中的一个连续元素序列。

示例 1:

输入:nums = [1,3,2,3,3], k = 2
输出:6
解释:包含元素 3 至少 2 次的子数组为:[1,3,2,3]、[1,3,2,3,3]、[3,2,3]、[3,2,3,3]、[2,3,3] 和 [3,3] 。

示例 2:

输入:nums = [1,4,2,1], k = 3
输出:0
解释:没有子数组包含元素 4 至少 3 次。

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 106
  • 1 <= k <= 105

题目地址:https://leetcode.cn/problems/count-subarrays-where-max-element-appears-at-least-k-times/?envType=daily-question&envId=2025-04-29

【滑动窗口】

public long countSubarrays(int[] nums, int k) {
    int max = Arrays.stream(nums).max().getAsInt();
    long res = 0;
    int l = 0, r = 0, maxNum = 0;
    while (true) {
        // 找到以l为左边界,满足条件的最小子数组(包左不包右)
        while (r < nums.length && maxNum < k) {
            if (nums[r++] == max) {
                maxNum++;
            }
        }
        // 如果当前子数组满足条件,那么左边界为l,右边界大于r的子数组都满足条件
        if (maxNum == k) {
            res += nums.length - r + 1;
        }
        // 当前左边界所有满足条件的子数组都统计完,左边界前进一位,如果跳过的是最大值,子数组内的最大值数目减一
        if (nums[l++] == max) {
            maxNum--;
        }
        // 如果右边界已经遍历到结尾,并且左边界已经移动到了子数组内没有足够的最大值,遍历结束
        if (maxNum < k && r == nums.length) break;
    }
    return res;
}

4月30日 leetcode 1295. 统计位数为偶数的数字(简)

【题目描述】

给你一个整数数组 nums,请你返回其中包含 偶数 个数位的数字的个数。

示例 1:

输入:nums = [12,345,2,6,7896]
输出:2
解释:
12 是 2 位数字(位数为偶数) 
345 是 3 位数字(位数为奇数)  
2 是 1 位数字(位数为奇数) 
6 是 1 位数字 位数为奇数) 
7896 是 4 位数字(位数为偶数)  
因此只有 12 和 7896 是位数为偶数的数字

示例 2:

输入:nums = [555,901,482,1771]
输出:1 
解释: 
只有 1771 是位数为偶数的数字。

提示:

  • 1 <= nums.length <= 500
  • 1 <= nums[i] <= 105

题目地址:https://leetcode.cn/problems/find-numbers-with-even-number-of-digits/?envType=daily-question&envId=2025-04-30

【解答】

public int findNumbers(int[] nums) {
    int res = 0;
    for (int num : nums) {
        String str = Integer.toString(num);
        if (str.length() % 2 == 0) {
            res++;
        }
    }
    return res;
}

5月5日 leetcode 790. 多米诺和托米诺平铺(中)

假期结束。。。。。。
一眼动态规划,但已经完全不会啦,没想到才一年大脑退化这么严重了,哈哈哈哈哈

【题目描述】

有两种形状的瓷砖:一种是 2 x 1 的多米诺形,另一种是形如 “L” 的托米诺形。两种形状都可以旋转。

在这里插入图片描述

给定整数 n ,返回可以平铺 2 x n 的面板的方法的数量。返回对 109 + 7 取模 的值。

平铺指的是每个正方形都必须有瓷砖覆盖。两个平铺不同,当且仅当面板上有四个方向上的相邻单元中的两个,使得恰好有一个平铺有一个瓷砖占据两个正方形。

示例 1:

在这里插入图片描述

输入: n = 3
输出: 5
解释: 五种不同的方法如上所示。

示例 2:

输入: n = 1
输出: 1

提示:

  • 1 <= n <= 1000

题目地址:https://leetcode.cn/problems/domino-and-tromino-tiling/description/?envType=daily-question&envId=2025-05-05

【动态规划】

假设当前铺设到第i列(第i列的任意正方形上有瓷砖就表示第i列铺设完成),则第i列铺设完成后的两个正方形有4种覆盖状态:

  • 状态0:两个正方形都未被覆盖。
  • 状态1:上方的正方形被覆盖。
  • 状态2:下方的正方形被覆盖。
  • 状态3:两个正方形都被覆盖。

dp[i][j]表示平铺到第i列时,状态为j的铺设方法。

  • 状态0:可以由状态为3的第i-1列不进行任何铺设得到。

    dp[i][0] = dp[i - 1][3]
    
  • 状态1:可以由状态为0的第i-1列铺设一个托米诺、状态为2的第i-1列铺设一个多米诺得到。

    dp[i][1] = dp[i - 1][0] + dp[i - 1][2]
    
  • 状态2:可以由状态为0的第i-1列铺设一个托米诺、状态为1的第i-1列铺设一个多米诺得到。

    dp[i][2] = dp[i - 1][0] + dp[i - 1][1]
    
  • 状态3:可以由状态为0的第i-1列铺设两个多米诺、状态为1的第i-1列铺设一个托米诺、状态为2的第i-1列铺设一个托米诺、状态为3的第i-1列再铺设一个多米诺得到。

    dp[i][3] = dp[i - 1][0] + dp[i - 1][1] + dp[i - 1][2] + dp[i - 1][3]
    

在这里插入图片描述

初始状态(第0列作为边界,第1列为数组的第0列):

dp[0][0] = 0;
dp[0][1] = 0;
dp[0][2] = 0;
dp[0][3] = 1;

题解:

public static final int MOD = 1000000007;

public int numTilings(int n) {
    int[][] dp = new int[n + 1][4];
    dp[0][3] = 1;
    for (int i = 1; i <= n; i++) {
        dp[i][0] = dp[i - 1][3];
        dp[i][1] = (dp[i - 1][0] + dp[i - 1][2]) % MOD;
        dp[i][2] = (dp[i - 1][0] + dp[i - 1][1]) % MOD;
        dp[i][3] = (((dp[i - 1][0] + dp[i - 1][1]) % MOD + dp[i - 1][2]) % MOD + dp[i - 1][3]) % MOD;
    }
    return dp[n][3];
}

参考答案:https://leetcode.cn/problems/domino-and-tromino-tiling/solutions/1962465/duo-mi-nuo-he-tuo-mi-nuo-ping-pu-by-leet-7n0j/?envType=daily-question&envId=2025-05-05

5月6日 leetcode 1920. 基于排列构建数组(简)

看到题目要求O(1)空间复杂度,残存的记忆立刻想到直接在原数组上构建,但也就到此为止了,哈哈哈

【题目描述】

给你一个 从 0 开始的排列 nums下标也从 0 开始)。请你构建一个 同样长度 的数组 ans ,其中,对于每个 i0 <= i < nums.length),都满足 ans[i] = nums[nums[i]] 。返回构建好的数组 ans

从 0 开始的排列 nums 是一个由 0nums.length - 10nums.length - 1 也包含在内)的不同整数组成的数组。

示例 1:

输入:nums = [0,2,1,5,3,4]
输出:[0,1,2,4,5,3]
解释:数组 ans 构建如下:
ans = [nums[nums[0]], nums[nums[1]], nums[nums[2]], nums[nums[3]], nums[nums[4]], nums[nums[5]]]
    = [nums[0], nums[2], nums[1], nums[5], nums[3], nums[4]]
    = [0,1,2,4,5,3]

示例 2:

输入:nums = [5,0,1,2,3,4]
输出:[4,5,0,1,2,3]
解释:数组 ans 构建如下:
ans = [nums[nums[0]], nums[nums[1]], nums[nums[2]], nums[nums[3]], nums[nums[4]], nums[nums[5]]]
    = [nums[5], nums[0], nums[1], nums[2], nums[3], nums[4]]
    = [4,5,0,1,2,3]

提示:

  • 1 <= nums.length <= 1000
  • 0 <= nums[i] < nums.length
  • nums 中的元素 互不相同

**进阶:**你能在不使用额外空间的情况下解决此问题吗(即 O(1) 内存)?

题目地址:https://leetcode.cn/problems/build-array-from-permutation/description/?envType=daily-question&envId=2025-05-06

【解答】

public int[] buildArray(int[] nums) {
    int[] res = new int[nums.length];
    for (int i = 0; i < nums.length; i++) {
        res[i] = nums[nums[i]];
    }
    return res;
}

【原地构建】

重新构造nums,让每个元素nums[i]能同时存储nums[i]nums[nums[i]]

对于每个元素,用该元素重构后的值/1000的值表示nums[i],用该元素重构后的值%1000表示nums[nums[i]]

public int[] buildArray(int[] nums) {
    int n = nums.length;
    for (int i = 0; i < n; ++i) {
        nums[i] += 1000 * (nums[nums[i]] % 1000);
    }
    for (int i = 0; i < n; ++i) {
        nums[i] /= 1000;
    }
    return nums;
}

参考答案:https://leetcode.cn/problems/build-array-from-permutation/solutions/858017/ji-yu-pai-lie-gou-jian-shu-zu-by-leetcod-gjcn/?envType=daily-question&envId=2025-05-06

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值