Leetcode - 周赛430

目录

[一,3402. 使每一列严格递增的最少操作次数](#一,3402. 使每一列严格递增的最少操作次数)

[二,3403. 从盒子中找出字典序最大的字符串 I](#二,3403. 从盒子中找出字典序最大的字符串 I)

[三,3404. 统计特殊子序列的数目](#三,3404. 统计特殊子序列的数目)

[四,3405. 统计恰好有 K 个相等相邻元素的数组数目](#四,3405. 统计恰好有 K 个相等相邻元素的数组数目)


一,3402. 使每一列严格递增的最少操作次数

将每一列变成严格递增的,且只能使用加法操作,需要的操作次数。直接依次枚举每一列:

  • 对于 nums[0],它肯定不变(只能执行加法操作)
  • 对于 nums[i:],如果 nums[i] > nums[i-1],不需要操作;如果 nums[i] <= nums[i-1],必须要将 nums[i] 变成 nums[i-1] + 1,也就是增加 nums[i-1] + 1 - nums[i]

代码如下:

class Solution {
    public int minimumOperations(int[][] grid) {
        int ans = 0;
        for(int j=0; j<grid[0].length; j++){
            int pre = grid[0][j];
            for(int i=1; i<grid.length; i++){
                ans += Math.max(pre - grid[i][j] + 1, 0);
                pre = Math.max(pre+1, grid[i][j]);
            }
        }
        return ans;
    }
}

二,3403. 从盒子中找出字典序最大的字符串 I

求字典序最大的字符串,如果枚举左右端点,需要 O(n^3) 的时间复杂度(比较字符串大小需要O(n)),会超时,有没有更快的做法?

可以发现在字符串前缀相同的情况下,字符串的长度一定是越长越好,也就是说,可以直接枚举左端点,右端点 = 左端点 + 字符串的最大长度 ,注意这里的右端点要和 word.length 取一个较小值。

那么本题字符串最长可以取到哪里,题目说将 word 分成 numFriends 段,取最长,那么将 numsFriends - 1 段的长度取 1,可以取到的最长长度就是 word.length - (numsFriends - 1) 。

代码如下:

class Solution {
    public String answerString(String s, int op) {
        if(op == 1) return s;
        int n = s.length();
        int mx = n - (op - 1);
        String ans = "";
        for(int i=0; i<n; i++){
            String sub = s.substring(i, Math.min(i+mx,n));
            if(sub.compareTo(ans) > 0){
                ans = sub;
            }
        }
        return ans;
    }
}

三,3404. 统计特殊子序列的数目

本题求满足 nums[p] * nums[r] = nums[q] * nums[s] 且 p < r < q < s 的子序列的个数,假设按照从前往后的顺序将四个数分为 a,b,c,d,也就是求 a * c = b * d 的子序列个数,将该式进行转换,得到 a / b = d / c,这样就按下标顺序把 a,b 放到一起,c,d 放到一起,可以用前后缀来做了。

代码如下:

class Solution {
    public long numberOfSubsequences(int[] nums) {
        int n = nums.length;
        Map<Float, Integer> map = new HashMap<>();
        long ans = 0;
        for(int i=2; i<n-4; i++){
            float b = nums[i];
            for(int j=0; j<i-1; j++){ 
                float a = nums[j];
                map.merge(a/b, 1, Integer::sum);
            }
            float c = nums[i+2];
            for(int j=i+4; j<n; j++){
                float d = nums[j];
                ans += map.getOrDefault(d/c, 0);
            }
        }
        return ans;
    }
}

上述做法是建立在数据范围小,使用 float 精确度足够的情况下,还有一种更加通用的做法,,将 :b: 通分一下,就是 ,这样可以存储 1/3 这种数字,代码如下:

class Solution {
    public long numberOfSubsequences(int[] nums) {
        int n = nums.length;
        Map<Integer, Integer> map = new HashMap<>();
        long ans = 0;
        for(int i=2; i<n-4; i++){
            int b = nums[i];
            for(int j=0; j<i-1; j++){ 
                int a = nums[j];
                int g = gcd(a, b);
                map.merge((a/g)<<16|(b/g), 1, Integer::sum);
            }
            int c = nums[i+2];
            for(int j=i+4; j<n; j++){
                int d = nums[j];
                int g = gcd(c, d);
                ans += map.getOrDefault((d/g)<<16|(c/g), 0);
            }
        }
        return ans;
    }
    int gcd(int x, int y){
        return y==0?x:gcd(y,x%y);
    }
}

四,3405. 统计恰好有 K 个相等相邻元素的数组数目

本题就是单纯的排列组合,题目要求恰好有 k 个下标满足 arr[i-1] = arr[i],对于一个数组 nums 来说,他一共有 n - 1 个相邻位置,其中 k 个位置要使得 arr[i-1] = arr[i],反过来就是有 n - 1 - k 个位置 arr[i-1] != arr[i],即在 n - 1 个位置选择 n - 1 - k 个位置来插排,且相邻的两个部分元素不同,对于第一个部分可以选择 m 个数,第二个部分可以选择 m-1 个数,第三、四、...... n-k 个部分都可以选择 m-1 个数。即

代码如下:

class Solution {
    private static final int MOD = 1_000_000_007;
    private static final int MX = 100_000;
    private static final long[] fac = new long[MX];
    private static final long[] invf = new long[MX];
    //1/(a * b) % MOD = pow(a*b, MOD-2), MOD必须是质数 
    //invf[i] = 1 / i! % MOD = pow(i!, MOD-2)
    //invf[i-1] = 1/(i-1)! % MOD = pow((i-1)!, MOD-2)
    //invf[i-1] = invf[i] * i
    static{
        fac[0] = 1;
        for(int i=1; i<MX; i++){
            fac[i] = fac[i-1] * i % MOD;
        }
        invf[MX - 1] = pow(fac[MX-1], MOD-2);
        for(int i=MX-1; i>0; i--){
            invf[i-1] = invf[i] * i % MOD;
        }
    }
    static long pow(long a, int b){
        long res = 1;
        while(b > 0){
            if(b % 2 == 1)
                res = res * a % MOD;
            a = a * a % MOD;
            b /= 2;
        }
        return res;
    }
    private long comb(int n, int m){
        // n!/m!*(n-m)!
        return fac[n] * invf[m] % MOD * invf[n-m] % MOD;
    }
    public int countGoodArrays(int n, int m, int k) {
        return (int)(comb(n-1, k) * m % MOD * pow(m-1, n-1-k) % MOD);
    }
}
相关推荐
IT猿手8 分钟前
超多目标优化:基于导航变量的多目标粒子群优化算法(NMOPSO)的无人机三维路径规划,MATLAB代码
人工智能·算法·机器学习·matlab·无人机
Erik_LinX18 分钟前
算法日记25:01背包(DFS->记忆化搜索->倒叙DP->顺序DP->空间优化)
算法·深度优先
Alidme25 分钟前
cs106x-lecture14(Autumn 2017)-SPL实现
c++·学习·算法·codestepbystep·cs106x
小王努力学编程26 分钟前
【算法与数据结构】单调队列
数据结构·c++·学习·算法·leetcode
最遥远的瞬间28 分钟前
15-贪心算法
算法·贪心算法
菜还不练就废了40 分钟前
蓝桥杯刷题25.2.22|打卡
职场和发展·蓝桥杯
程序员 小濠1 小时前
接口测试基础 --- 什么是接口测试及其测试流程?
自动化测试·python·测试工具·职场和发展·appium·接口测试·压力测试
维齐洛波奇特利(male)1 小时前
(动态规划 完全背包 **)leetcode279完全平方数
算法·动态规划
大米洗澡2 小时前
数字签名技术基础
python·学习·程序人生·面试·职场和发展
项目申报小狂人3 小时前
改进收敛因子和比例权重的灰狼优化算法【期刊论文完美复现】(Matlab代码实现)
开发语言·算法·matlab