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

LeetCode --- 421周赛

题目列表

3334. 数组的最大因子得分

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

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

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

一、数组的最大因子得分

数据范围足够小,可以用暴力枚举移除的数字,得到答案,时间复杂度为O(n^2),代码如下

class Solution {
public:
    long long maxScore(vector<int>& nums) {
        int n = nums.size();
        auto get = [&](int i)->long long{
            // gcd(0, x) = x, lcm(1, x) = x
            long long x = 0; // 计算 gcd
            long long y = 1; // 计算 lcm
            for(int j = 0; j < n; j++){
                if(i == j) continue;
                x = gcd(x, nums[j]);
                y = lcm(y, nums[j]);
            }
            return x * y;
        };
        long long ans = get(-1); // 不去除任何数字
        for(int i = 0; i < n; i++){
            ans = max(ans, get(i));
        }
        return ans;
    }
};

有没有更快的做法?我们同样枚举被移除的数字,有没有方法能更加快速的算出剩余数字的 LCM 和 GCD?有的,只要我们提前算出左右两个部分的 LCM 和 GCD,就能直接计算得出剩余部分的LCM 和 GCD,即进行前后缀分解,时间复杂度为O(n),代码如下

注意:上面的时间复杂度默认 LCM 和 GCD 是O(1),但实际上 GCD/LCM 的时间复杂度为O(logn)

class Solution {
public:
    long long maxScore(vector<int>& nums) {
        int n = nums.size();
        vector<long long> suf_gcd(n + 1), suf_lcm(n + 1, 1);
        // gcd(0, x) = x, lcm(1, x) = x
        for(int i = n - 1; i >= 0; i--){
            suf_gcd[i] = gcd(suf_gcd[i + 1], nums[i]);
            suf_lcm[i] = lcm(suf_lcm[i + 1], nums[i]);
        }
        long long ans = suf_gcd[0] * suf_lcm[0]; // 不去除任何数
        long long pre_gcd = 0, pre_lcm = 1;
        for(int i = 0; i < n; i++){ // 同时计算 ans 和 前缀gcd/lcm
            ans = max(ans, gcd(pre_gcd, suf_gcd[i + 1]) * lcm(pre_lcm, suf_lcm[i+1]));
            pre_gcd = gcd(pre_gcd, nums[i]);
            pre_lcm = lcm(pre_lcm, nums[i]);
        }
        return ans;
    }
};

二、字符串转换后的长度 I

这题的数据范围比较小,我们可以模拟 t 次转换的过程。对于任意一个字母,它的转换规则是一样的,所以我们先统计出 26 个字母出现的次数,然后根据规则,进行转换即可,代码如下

class Solution {
    const int MOD = 1e9 + 7;
public:
    int lengthAfterTransformations(string s, int t) {
        vector<int> cnt(26);
        for(auto e : s) cnt[e - 'a']++;
        while(t--){
            vector<int>tmp(26);
            for(int i = 0; i < 26; i++)
                tmp[i] = cnt[(i-1+26)%26]; // 如'a'的出现次数变成'b'的出现次数
            // 'z' 不仅能变成 'a' , 还能变成 'b'
            tmp[1] =(tmp[1] + cnt[25]) % MOD;
            swap(tmp, cnt);
        }
        int ans = 0;
        for(int i = 0; i < 26; i++) ans = (ans + cnt[i]) % MOD;
        return ans;
    }
};

但是一旦 t 的范围过大,就会超时,有没什么更快的方法?由于每个字母的转移方式是固定的,所以只要给定一个字母和操作次数就能得到一个长度,问题是如何加速这个计算过程?

假设f[i][j]表示字母 i (用0-25表示) 经过 j 次操作的长度,我们有如下方程

代码如下

class Solution {
    const int MOD = 1e9 + 7;
    // 矩阵快速幂
    vector<vector<int>> POW(vector<vector<int>> a, int n){
        int m = a.size();
        vector<vector<int>> res(m, vector<int>(m));
        for(int i = 0; i < m; i++) res[i][i] = 1;
        while(n){
            if(n & 1) res = mul(res, a);
            a = mul(a, a);
            n >>= 1;
        }
        return res;
    }
    // 矩阵相乘
    vector<vector<int>> mul(const vector<vector<int>>& a, const vector<vector<int>>& b){
        int n = a.size(), m = b[0].size();
        vector<vector<int>> c(n, vector<int>(m));
        for(int i = 0; i < n; i++){
            for(int k = 0; k < n; k++){
                if(a[i][k] == 0) continue;
                for(int j = 0; j < m; j++){
                    c[i][j] = (c[i][j] + 1LL * a[i][k] * b[k][j]) % MOD;
                }
            }
        }
        return c;
    }
public:
    int lengthAfterTransformations(string s, int t) {
        int n = s.size();
        vector<vector<int>> mtx(26, vector<int>(26));
        for(int i = 0; i < 26; i++){
            mtx[i][(i+1)%26] = 1;
        }
        mtx[25][1] = 1;
        auto f = POW(mtx, t); // 矩阵的t次幂
        vector<int> cnt(26);
        for(auto e : s) cnt[e - 'a']++;
        long long ans = 0;
        for(int i = 0; i < 26; i++){
            ans += reduce(f[i].begin(), f[i].end(), 0LL) * cnt[i];
        }
        return ans % MOD;
    }
};

三、最大公约数相等的子序列数量

对于每一个数,都有三种可能,要么在seq1,要么在seq2,要么不选,一旦我们选择完一个数,对于剩下的数,我们依旧可以用相同的方法进行处理,大问题被划分为一个个小问题,进行解决。

设dfs(i,j,k)表示当seq1的gcd=j,seq2的gcd=k时,从前 i 个数中进行选择能得到的合法方案数

对于 nums[i]

  • 1、不选,方案数为 dfs(i-1,j,k)
  • 2、选入seq1,方案数为 dfs(i-1,gcd(j,nums[i]),k)
  • 3、选入seq2,方案数为 dfs(i-1,j,gcd(k,nums[i])) 

故状态转换方程为

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])) 

边界条件:当 i < 0 时,返回 j == k,表示将所有的数都进行分配后,如果seq1的gcd = seq2的gcd,则为一种合法方案数

代码如下

class Solution {
    const int MOD = 1e9 + 7;
public:
    int subsequencePairCount(vector<int>& nums) {
        int n = nums.size();
        int memo[n][201][201];
        memset(memo, -1, sizeof(memo));
        function<int(int,int,int)> dfs = [&](int i, int j, int k)->int{
            if(i < 0) return j == k;
            if(memo[i][j][k] != -1) return memo[i][j][k];
            int res = dfs(i - 1, j, k); // 不选
            res = (res + dfs(i - 1, gcd(j, nums[i]), k)) % MOD;
            res = (res + dfs(i - 1, j, gcd(k, nums[i]))) % MOD;
            return memo[i][j][k] = res;
        };
        // 注意我们的dfs会包含一种seq1和seq2都为空的方案,需要被减去
        // 由于取模操作 dfs(n - 1, 0, 0) - 1 可能为负数,所以要 + MOD) % MOD
        return (dfs(n - 1, 0, 0) - 1 + MOD) % MOD;
    }
};

四、字符串转换后的长度 II

这题的思路同第二题,只是计算的矩阵不同,具体代码如下

class Solution {
    const int MOD = 1e9 + 7;
    // 矩阵快速幂
    vector<vector<int>> POW(vector<vector<int>> a, int n){
        int m = a.size();
        vector<vector<int>> res(m, vector<int>(m));
        for(int i = 0; i < m; i++) res[i][i] = 1;
        while(n){
            if(n & 1) res = mul(res, a);
            a = mul(a, a);
            n >>= 1;
        }
        return res;
    }
    // 矩阵相乘
    vector<vector<int>> mul(const vector<vector<int>>& a, const vector<vector<int>>& b){
        int n = a.size(), m = b[0].size();
        vector<vector<int>> c(n, vector<int>(m));
        for(int i = 0; i < n; i++){
            for(int k = 0; k < n; k++){
                if(a[i][k] == 0) continue;
                for(int j = 0; j < m; j++){
                    c[i][j] = (c[i][j] + 1LL * a[i][k] * b[k][j]) % MOD;
                }
            }
        }
        return c;
    }
public:
    int lengthAfterTransformations(string s, int t, vector<int>& nums) {
        int n = s.size();
        vector<vector<int>> mtx(26, vector<int>(26));
        for(int i = 0; i < 26; i++){
            for(int j = i + 1; j <= i + nums[i]; j++){
                mtx[i][j%26] = 1;
            }
        }
        auto f = POW(mtx, t);
        vector<int> cnt(26);
        for(auto e : s) cnt[e - 'a']++;
        long long ans = 0;
        for(int i = 0; i < 26; i++){
            ans += reduce(f[i].begin(), f[i].end(), 0LL) * cnt[i];
        }
        return ans % MOD;
    }
};

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

相关文章:

  • 智能家居10G雷达感应开关模块,飞睿智能uA级别低功耗、超高灵敏度,瞬间响应快
  • 高并发-负载均衡
  • 【Coroutines】Full Understanding of Kotlinx.Corutines Framework
  • 【第一个qt项目的实现和介绍以及程序分析】【正点原子】嵌入式Qt5 C++开发视频
  • 四足机器人实战篇之二十二:四足机器人支撑腿反作用力规划之反馈控制及线性约束条件优化方法
  • Video Posts
  • linux开机自启动三种方式
  • PySpark的使用
  • 一、Go语言快速入门之基础语法
  • python的socket库的基本使用总目录
  • 大语言模型推理源码解读(基于llama3模型:来源github)
  • SpringBoot旋律线:Web音乐网站构建
  • 基于SSM医药进出口交易系统的设计
  • 无线基础配置
  • 深入解析C/C++中的__attribute__((packed)):内存对齐与紧打包技术
  • 目录遍历漏洞
  • AE制作太阳光线穿过手指间隙的教程
  • A*算法求第k短路
  • 机器学习:我们能用机器学习来建立投资模型吗
  • 解决宝塔安装wxwork_finance_sdk出现free():invalid pointer Aborted (core dumped)
  • leetcode 2710 移除字符串中的尾随零
  • 车载总线系列 --- CAN FD简介
  • 【自动化运维从 0-1 保姆级教程-超细-超长篇】
  • 遥感辐射传输方程中的格林函数
  • 如何保护网站安全
  • 大家觉得做一个大模型检索增强生成(RAG)系统,最难搞定的是那部分工作?