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);
    }
}
相关推荐
xyliiiiiL9 分钟前
ZGC初步了解
java·jvm·算法
爱的叹息38 分钟前
RedisTemplate 的 6 个可配置序列化器属性对比
算法·哈希算法
独好紫罗兰1 小时前
洛谷题单2-P5713 【深基3.例5】洛谷团队系统-python-流程图重构
开发语言·python·算法
每次的天空2 小时前
Android学习总结之算法篇四(字符串)
android·学习·算法
请来次降维打击!!!2 小时前
优选算法系列(5.位运算)
java·前端·c++·算法
qystca2 小时前
蓝桥云客 刷题统计
算法·模拟
别NULL3 小时前
机试题——统计最少媒体包发送源个数
c++·算法·媒体
weisian1513 小时前
Java常用工具算法-3--加密算法2--非对称加密算法(RSA常用,ECC,DSA)
java·开发语言·算法
程序员黄同学4 小时前
贪心算法,其优缺点是什么?
算法·贪心算法
SsummerC5 小时前
【leetcode100】每日温度
数据结构·python·leetcode