当前位置: 首页 > article >正文

Leetcode - 周赛421

目录

一,3334. 数组的最大因子得分

二,3335. 字符串转换后的长度 I

三,3336. 最大公约数相等的子序列数量

四,3337. 字符串转换后的长度 II


一,3334. 数组的最大因子得分

暴力方法就不演示,这里介绍一个O(n)的做法,前后缀分解,可以预处理nums数组的前缀GCD,LCM和后缀GCD,LCM。然后枚举移除的数,使用前后缀计算总体的GCD和LCM,然后计算出最大值。

代码如下:

class Solution {
    public long maxScore(int[] nums) {
        int n = nums.length;
        long[] sufGcd = new long[n+1];
        long[] sufLcm = new long[n+1];
        sufLcm[n] = 1;
        for(int i=n-1; i>=0; i--){
            sufGcd[i] = gcd(sufGcd[i+1], nums[i]);
            sufLcm[i] = lcm(sufLcm[i+1], nums[i]);
        }
        long res = sufGcd[0]*sufLcm[0], gc = 0, lc = 1;
        for(int i=0; i<n; i++){//枚举哪个不选
            res = Math.max(res, gcd(gc, sufGcd[i+1]) * lcm(lc, sufLcm[i+1]));
            lc = lcm(lc, nums[i]);
            gc = gcd(gc, nums[i]);
        }
        return res;
    }
    long gcd(long x, long y){//(0,x)的最大公约数就是x
        return y==0 ? x : gcd(y, x%y);
    }
    long lcm(long x, long y){//(1,x)的最小公倍数就是x
        return x*y/gcd(x, y);
    }
}

二,3335. 字符串转换后的长度 I

本题有多种做法,这里讲一个简单易懂的,可以发现对于每个字符,它们每次操作产生的字符数量是固定的,所以我们可以先统计26个字符,每个字符的出现次数cnt,然后模拟每次操作所能产生的字符数量,最后cnt数组的和就是答案。

代码如下:

class Solution {
    int MOD = 1_000_000_007;
    public int lengthAfterTransformations(String s, int t) {
        int ans = 0;
        int[] cnt = new int[26];
        for(char c : s.toCharArray()){
            cnt[c-'a']++;
        }
        while(t-- > 0){
            int[] a = cnt.clone();
            for(int i=1; i<26; i++){
                a[i] = cnt[i-1]%MOD;
            }
            a[0] = cnt[25]%MOD;
            a[1] = (a[1] + cnt[25])%MOD;
            cnt = a;
        }
        for(int i=0; i<26; i++){
            ans = (ans + cnt[i])%MOD;
        }
        return ans;
    }
}

三,3336. 最大公约数相等的子序列数量

本题是一道选或不选的问题,分情况讨论,对于第 i 个数:

  • 如果不选,即它既不在seq1中,也不在seq2中,接下来问题变成在第 i+1 个数到第 n-1 个数,有多少种分配使得子序列seq1和seq2的GCD相同
  • 如果选,分两种情况,它被分配到seq1/seq2中,接下来问题变成在第 i+1 个数到第 n-1 个数,有多少种分配使得子序列seq1和seq2的GCD相同

这样就不断的形成子问题,定义dfs(i,j,k):从第 i 个数到第 n-1 个数,且此时seq1的GCD为 j ,seq2的GCD为 k 时,使得子序列seq1和seq2的GCD相同的子序列对的总数。

  • 如果不选,问题变成在第 i+1 个数到第 n-1 个数,有多少种分配使得子序列seq1和seq2的GCD相同,即 dfs(i+1,j,k)
  • 如果选,分两种情况,它被分配到seq1/seq2中,问题变成在第 i+1 个数到第 n-1 个数,有多少种分配使得子序列seq1和seq2的GCD相同,即 dfs(i+1,gcd(j,nums[i]),k) + dfs(i+1, j, gcd(k,nums[i]),nums)
  • 求总对数 dfs(i,j,k) = dfs(i+1,j,k) + dfs(i+1,gcd(j,nums[i]),k) + dfs(i+1, j, gcd(k,nums[i]),nums)

代码如下:

class Solution {
    int MOD = 1_000_000_007;
    public int subsequencePairCount(int[] nums) {
        int n = nums.length;
        memo = new int[n][201][201];
        for(int i=0; i<n; i++){
            for(int[] r : memo[i]){
                Arrays.fill(r, -1);
            }
        }
        return (dfs(0, 0, 0, nums)-1+MOD)%MOD;//-1是删除全部都不选的情况
    }
    int[][][] memo;
    int dfs(int i, int j, int k, int[] nums){
        if(i == nums.length) return j == k ? 1 : 0;
        if(memo[i][j][k] != -1) return memo[i][j][k];
        long res = (long)dfs(i+1, j, k, nums) + 
                        dfs(i+1, gcd(j, nums[i]), k, nums) + 
                        dfs(i+1, j, gcd(k, nums[i]), nums);
        return memo[i][j][k] = (int)(res%MOD);
    }
    int gcd(int x, int y){
        return y==0 ? x : gcd(y, x%y);
    }
} 

递推写法

class Solution {
    int MOD = 1_000_000_007;
    public int subsequencePairCount(int[] nums) {
        int n = nums.length;
        int m = 0;
        for(int x : nums) m = Math.max(x, m);
        long[][][] f = new long[n+1][m+1][m+1];
        //f[n][i][i] = 1
        for(int i=0; i<m+1; i++){
            f[n][i][i] = 1;
        }
        for(int i=n-1; i>=0; i--){
            for(int j=0; j<m+1; j++){
                for(int k=0; k<m+1; k++){
                    f[i][j][k] = (f[i+1][j][k] + f[i+1][gcd(j, nums[i])][k] + f[i+1][j][gcd(k, nums[i])])%MOD;
                }
            }
        }
        return (int)(f[0][0][0]-1+MOD)%MOD;
    }
    int gcd(int x, int y){
        return y==0 ? x : gcd(y, x%y);
    }
} 

四,3337. 字符串转换后的长度 II

本题与T2不同,对于每个字符,它们操作后会转换成连续的nums[i]个字符,且数据范围更大,无法使用上述做法。这里我们可以使用dfs来预处理每个字符,操作 t 次后,所能形成的字符串长度。

dfs记忆化代码:

class Solution {
    private static final int MOD = 1_000_000_007;
    public int lengthAfterTransformations(String s, int t, List<Integer> nums) {
        int[] cnt = new int[26];
        for(char x : s.toCharArray()){
            cnt[x-'a']++;
        }
        long ans = 0;
        memo = new int[t+1][26];
        for(int i=0; i<t+1; i++) 
            Arrays.fill(memo[i], -1);
        int[] res = new int[26];
        for(int i=0; i<26; i++){
            res[i] = dfs(t, i, nums);
            ans += ((long)res[i] * cnt[i])%MOD;
        }
        return (int)(ans%MOD);
    }
    int[][] memo;
    int dfs(int i, int j, List<Integer> nums){
        if(i == 0) return 1;
        if(memo[i][j] != -1) return memo[i][j];
        long res = 0;
        for(int x=1; x<=nums.get(j); x++){
            res = (res + dfs(i-1, (j+x)%26, nums))%MOD;
        }
        return memo[i][j] = (int)(res%MOD);
    }
}

但是上述做法的时间复杂度为O(26*26*t),这肯定会超时间限制,将其转换成dp再来观察观察是否有其他的优化空间(对于本题至少需要一个O(logt)的时间复杂度).

递推代码:

class Solution {
    private static final int MOD = 1_000_000_007;
    public int lengthAfterTransformations(String s, int t, List<Integer> nums) {
        int[] cnt = new int[26];
        for(char x : s.toCharArray()){
            cnt[x-'a']++;
        }
        long ans = 0;
        long[][] f = new long[t+1][26];
        for(int i=0; i<26; i++)
            f[0][i] = 1;
        //预处理每个字母处理t次后,字符串的长度
        //O(26*25*t)
        for(int i=1; i<t+1; i++){
            for(int j=0; j<26; j++){
                for(int x=1; x<=nums.get(j); x++){
                    f[i][j] = (f[i][j] + f[i-1][(j+x)%26])%MOD;
                }
            }
        }
        //O(1)
        for(int k=0; k<26; k++){
            ans += (f[t][k] * cnt[k])%MOD;
        }
        return (int)(ans%MOD);
    }
}

可以发现 f[i][j] = sum(f[i-1][(j+x)%26]),拿示例一举例:

nums = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2]

根据上述公式,可以得到:

将其转换成矩阵的样式:

将 fi0~fi25 矩阵统称为 Fi,那么我们可以得到公式 Fi = M * Fi-1,不断的套用公式,我们可以得到 Fi = M * M * Fi-2 = ... = M^t * F0

这时可以发现,M是一个固定的矩阵(它是通过nums数组计算得来的),M^t 可以使用快速幂来求解,不过这里计算的是矩阵快速幂。

矩阵快速幂优化代码:

class Solution {
    private static final int MOD = 1_000_000_007;
    public int lengthAfterTransformations(String s, int t, List<Integer> nums) {
        int[] cnt = new int[26];
        for(char x : s.toCharArray()){
            cnt[x-'a']++;
        }
        int[][] m = new int[26][26];
        for(int i=0; i<26; i++){
            for(int j=1; j<=nums.get(i); j++){
                m[i][(i+j)%26]++;
            }
        }        
        int[][] f = new int[26][1];
        for(int i=0; i<26; i++)
            f[i][0] = 1;
        m = pow(m, t, f);
        long ans = 0;
        for(int i=0; i<26; i++){
            System.out.println(cnt[i] + " " + m[i][0]);
            ans = (ans + (long)cnt[i] * m[i][0])%MOD;
        }
        return (int)(ans%MOD);
    }
    int[][] pow(int[][] m, int t, int[][] f){
        int[][] res = f;
        while(t != 0){
            if((t&1)!=0){
                res = mul(m, res);
            }
            m = mul(m, m);
            t >>= 1;
        }
        return res;
    }
    //矩阵的传参一定要注意:AxB * BxC = AxC !!!
    int[][] mul(int[][] a, int[][] b){
        int[][] c = new int[a.length][b[0].length];
        //c[i][j] += a[i][k] * b[k][j];
        for(int i=0; i<a.length; i++){
            for(int k=0; k<a[0].length; k++){
                if(a[i][k] == 0) continue;
                for(int j=0; j<b[0].length; j++){
                    c[i][j] = (int)((c[i][j] + (long)a[i][k] * b[k][j])%MOD);
                }
            }
        }
        return c;
    }
}

http://www.kler.cn/a/378376.html

相关文章:

  • 真·香!深度体验 zCloud 数据库云管平台 -- DBA日常管理篇
  • 基于人脸识别PCA算法matlab实现及详细步骤讲解
  • ubuntu20.04 加固方案-设置SSH是否使用业界认可的加密算法
  • 学webpack之loader原理,让面试官跪下来唱征服
  • 服务器新建用户
  • 一些常用的react hooks以及各自的作用
  • ChatGPT 越狱:研究人员使用十六进制编码和表情符号绕过 AI 保护措施
  • 双目视觉标定——1原理与实践
  • 软件测试--BUG篇
  • 基于Retinex算法的图像去雾matlab仿真
  • WPF 实现冒泡排序可视化
  • Pr 视频效果:ASC CDL
  • 基于物联网的户外环境检测装置教学文章
  • Qt中的Model与View 4:QStandardItemModel与QTableView
  • 【FL0013】基于SpringBoot和微信小程序的机电公司管理信息系统
  • 人工智能如何改变未来生活:从医疗到日常的全面升级
  • 项目学习总结
  • js中多let与var
  • 如何使用 EXPLAIN 分析查询计划?
  • 例行性工作
  • [LeetCode] 1137. 第N个泰波那契数
  • 串口屏控制的自动滑轨(未完工)
  • 【论文解读】EdgeYOLO:一种边缘实时目标检测器(附论文地址)
  • Django响应
  • 滑动窗口习题篇(上)
  • cookie、session、http简单理解