Leetcode - 周赛423

目录

[一,3349. 检测相邻递增子数组 I](#一,3349. 检测相邻递增子数组 I)

[二,3350. 检测相邻递增子数组 II](#二,3350. 检测相邻递增子数组 II)

[三,3351. 好子序列的元素之和](#三,3351. 好子序列的元素之和)

[四,3352. 统计小于 N 的 K 可约简整数](#四,3352. 统计小于 N 的 K 可约简整数)


一,3349. 检测相邻递增子数组 I

本题有两种做法:

  1. 先求出递增子数组 f (以 i 结尾的最长子数组长度),然后枚举前一个数组的末尾下标i,判断 f[i] 和 f[i+k] 是否都大于 k
  2. 求数组的两个子数组都是严格递增且相邻,有两种情况,要么它一直递增,那么最长可能满足条件的就是 cnt/2,要么它是相邻的两段递增(这两段数组长度求最小),画个图理解一下:

代码如下:

复制代码
//方法一
class Solution {
    public boolean hasIncreasingSubarrays(List<Integer> nums, int k) {
        int n = nums.size();
        int[] f = new int[n];
        f[0] = 1;
        for(int i=1; i<n; i++){
            if(nums.get(i) > nums.get(i-1)){
                f[i] = f[i-1] + 1;
            }else{
                f[i] = 1;
            }
        }
        for(int i=k-1; i<n-k; i++){
            if(f[i]>=k && f[i+k]>=k)
                return true;
        }
        return false;
    }
}
//方法二
class Solution {
    public boolean hasIncreasingSubarrays(List<Integer> nums, int k) {
        int ans = 1, cnt = 0, pre = 0;
        for(int i=0; i<nums.size(); i++){
            cnt++;
            if(i == nums.size()-1 || nums.get(i) >= nums.get(i+1)){
                ans = Math.max(ans, Math.max(cnt/2, Math.min(cnt, pre)));
                pre = cnt;
                cnt = 0;
            }
        }
        return ans >= k;
    }
}

二,3350. 检测相邻递增子数组 II

本题和T1有一点不同,它是求满足条件的最长子数组的长度,那么就是T1的方法二,直接返回ans就行,当然如果T1是用方法一写的,那么这题肯定会想到二分。

代码如下:

复制代码
class Solution {
    public int maxIncreasingSubarrays(List<Integer> nums) {
        int ans = 1, cnt = 0, pre = 0;
        for(int i=0; i<nums.size(); i++){
            cnt++;
            if(i == nums.size()-1 || nums.get(i) >= nums.get(i+1)){
                ans = Math.max(ans, Math.max(cnt/2, Math.min(cnt, pre)));
                pre = cnt;
                cnt = 0;
            }
        }
        return ans;
    }
}
//二分写法
class Solution {
    public int maxIncreasingSubarrays(List<Integer> nums) {
        int n = nums.size();
        int[] f = new int[n];
        f[0] = 1;
        for(int i=1; i<n; i++){
            if(nums.get(i) > nums.get(i-1)){
                f[i] = f[i-1] + 1;
            }else{
                f[i] = 1;
            }
        }
        int l = 1, r = n;
        while(l <= r){
            int k = (l + r) / 2;
            if(check(k, nums, f)){
                l = k + 1;
            }else{
                r = k - 1;
            }
        }
        return l - 1;
    }
    boolean check(int k, List<Integer> nums, int[] f){
        int n = nums.size();
        for(int i=k-1; i<n-k; i++){
            if(f[i]>=k && f[i+k]>=k)
                return true;
        }
        return false;
    }
}

三,3351. 好子序列的元素之和

定义 cnt[x]:以 x 结尾的子序列的个数,f[x]:所有以 x 结尾的子序列的和。枚举 nums 数组,对于当前元素 x 来说:

  • cnt[x] = cnt[x] + cnt[x-1] + cnt[x+1] + 1
  • f[x] = f[x] + f[x-1] + f[x+1] + (cnt[x-1] + cnt[x+1] + 1) * x
  • 答案就是 sum(f)

代码如下:

复制代码
class Solution {
    public int sumOfGoodSubsequences(int[] nums) {
        final int MOD = 1_000_000_007;
        Map<Integer, Integer> f = new HashMap<>();
        Map<Integer, Integer> cnt = new HashMap<>();
        for (int x : nums) {
            long c = cnt.getOrDefault(x - 1, 0) + cnt.getOrDefault(x + 1, 0) + 1;
            f.put(x, (int)((x * c + f.getOrDefault(x, 0) + f.getOrDefault(x - 1, 0) + f.getOrDefault(x + 1, 0)) % MOD));
            cnt.put(x, (int)((cnt.getOrDefault(x, 0) + c) % MOD));
        }
        long ans = 0;
        for (int s : f.values()) {
            ans += s;
        }
        return (int) (ans % MOD);
    }
}

四,3352. 统计小于 N 的 K 可约简整数

定义 f[x]:表示 x 二进制中 1 的个数,f*[x]:将 x 通过 f[x] 不断的迭代,将 x 迭代成 1 需要的操作次数。可以得到:f*[x] = f*[f(x)] + 1

通过上述公式可以算出二进制中有 x 个 1 的数需要操作 f*[x] 次才能转换成 1,本题问的是有多少个整数,可以使用数位DP进行计算,通过枚举每一个二进制位为 0 / 1,计算出总共有多少满足条件的整数,注:题目要求所有整数小于给的二进制数 s。

代码如下:

复制代码
class Solution {
    private static final int MOD = 1_000_000_007;
    public int countKReducibleNumbers(String s, int k) {
        int n = s.length();
        int ans = 0;
        int[] f = new int[n];
        memo = new int[n][n];
        for(int[] r : memo) Arrays.fill(r, -1);
        List<Integer> v = new ArrayList<>();
        for(int i=1; i<n; i++){
            f[i] = f[Integer.bitCount(i)] + 1;
            if(f[i] <= k){ 
                //原本f[1]=0,f[i] < k, 但是这里f[1]=1,相当于所有f[i]+1,所以<=k
                ans = (ans + dfs(0, i, true, s.toCharArray())) % MOD;
            }
        }
        return ans;
    }
    int[][] memo;
    int dfs(int i, int j, boolean islimit, char[] s){
        if(i == s.length) return !islimit&&j==0?1:0;//1.防止与s全部相同。2.使用了j个1
        if(!islimit && memo[i][j] != -1) return memo[i][j];
        int up = islimit ? s[i] - '0' : 1;
        int res = 0;
        for(int d=0; d<=Math.min(j, up); d++){
            res = (res + dfs(i+1, j-d, islimit && d==up, s)) % MOD;
        }
        if(!islimit) memo[i][j] = res;
        return res;
    }
}
相关推荐
AI科技星5 分钟前
科幻艺术书本封面:《全域数学》第一部·数术本源 第三卷 代数原本(P95-141)完整五级目录【乖乖数学】
算法·机器学习·数学建模·数据挖掘·量子计算
风筝在晴天搁浅7 分钟前
LeetCode 92.反转链表Ⅱ
算法·leetcode·链表
weisian15115 分钟前
基础篇--概念原理-2-参数是什么?——从原理到实战,一篇讲透
面试·职场和发展·模型参数·7b和70b·参数=规则,不是原始数据
王老师青少年编程21 分钟前
csp信奥赛C++高频考点专项训练之贪心算法 --【贪心与二分判定】:数列分段 Section II
c++·算法·贪心·csp·信奥赛·二分判定·数列分段 section ii
V搜xhliang024642 分钟前
OpenClaw科研全场景用法:从文献到实验室的完整自动化方案
运维·开发语言·人工智能·python·算法·microsoft·自动化
天真小巫1 小时前
2026.5.2总结
职场和发展
汉克老师1 小时前
GESP2025年3月认证C++五级( 第三部分编程题(2、原根判断))
c++·算法·模运算·gesp5级·gesp五级·原根·分解质因数
weisian1511 小时前
基础篇--概念原理-1-Token是什么?——从原理到实战,一篇讲透
人工智能·职场和发展·token
数据皮皮侠1 小时前
上市公司创新韧性数据(2000-2024)|顶刊同款 EIR 指数
大数据·人工智能·算法·智慧城市·制造
WL_Aurora1 小时前
Python 算法基础篇之链表
python·算法·链表