• Leetcode 第 368 场周赛题解


    Leetcode 第 368 场周赛题解

    题目1:2908. 元素和最小的山形三元组 I

    思路

    暴力枚举下标三元组 (i, j, k),更新答案 sum = min(sum, nums[i] + nums[j] + nums[k])。

    代码

    /*
     * @lc app=leetcode.cn id=2908 lang=cpp
     *
     * [2908] 元素和最小的山形三元组 I
     */
    
    // @lc code=start
    class Solution
    {
    public:
        int minimumSum(vector<int> &nums)
        {
            int n = nums.size();
            int sum = INT_MAX;
            for (int i = 0; i < n - 2; i++)
                for (int j = i + 1; j < n - 1; j++)
                    for (int k = j + 1; k < n; k++)
                        if (nums[i] < nums[j] && nums[j] > nums[k])
                            sum = min(sum, nums[i] + nums[j] + nums[k]);
            return sum == INT_MAX ? -1 : sum;
        }
    };
    // @lc code=end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    复杂度分析

    时间复杂度:O(n3),其中 n 是数组 nums 的长度。

    空间复杂度:O(1)。

    题目2:2909. 元素和最小的山形三元组 II

    思路

    枚举 nums[j] + 前后缀分解。

    定义 preMin[i] 为前缀最小值,初始化 preMin[0] = nums[0],递推公式:preMin[i] = min(preMin[i - 1], nums[i])

    定义 sufMin[i] 为后缀最小值,初始化 sufMin[n - 1] = nums[n - 1],递推公式:sufMin[i] = min(sufMin[i + 1], nums[i])

    枚举 nums[j],答案为 preMin[j - 1] + nums[j] + sufMin[j + 1] 的最小值。

    代码

    /*
     * @lc app=leetcode.cn id=2909 lang=cpp
     *
     * [2909] 元素和最小的山形三元组 II
     */
    
    // @lc code=start
    class Solution
    {
    public:
        int minimumSum(vector<int> &nums)
        {
            int n = nums.size();
            vector<int> preMin(n);
            preMin[0] = nums[0];
            for (int i = 1; i < n; i++)
                preMin[i] = min(preMin[i - 1], nums[i]);
            vector<int> sufMin(n);
            sufMin[n - 1] = nums[n - 1];
            for (int i = n - 2; i >= 0; i--)
                sufMin[i] = min(sufMin[i + 1], nums[i]);
            int minimumSum = INT_MAX;
            for (int j = 1; j < n - 1; j++)
                if (preMin[j - 1] < nums[j] && nums[j] > sufMin[j + 1])
                    minimumSum = min(minimumSum, preMin[j - 1] + nums[j] + sufMin[j + 1]);
            return minimumSum == INT_MAX ? -1 : minimumSum;
        }
    };
    // @lc code=end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    复杂度分析

    时间复杂度:O(n),其中 n 是数组 nums 的长度。

    空间复杂度:O(n),其中 n 是数组 nums 的长度。

    题目3:2910. 合法分组的最少组数

    思路

    贪心。

    统计每个数字的出现次数,记在哈希表 hash 中。

    假设可以分成大小为 k 和 k+1 的组,现在需要算出对于每个数 num,每个 hash[num] 最少可以分成多少组。

    设 q = freq / k,r = freq % k。

    如果 q < r 则无法分成 k 和 k+1 组,否则一定可以分组。

    在可以分组的前提下,分出的 k+1 越多,组数就越少,所以最少可以分出 ceil(freq / (k + 1)) 组。

    累加组数即为分组个数。

    从 min⁡(hash[num]) 开始倒着枚举 k,只要可以分,就立刻返回答案。

    代码

    /*
     * @lc app=leetcode.cn id=2910 lang=cpp
     *
     * [2910] 合法分组的最少组数
     */
    
    // @lc code=start
    class Solution
    {
    public:
        int minGroupsForValidAssignment(vector<int> &nums)
        {
            unordered_map<int, int> hash; // 
            for (const int &num : nums)
                hash[num]++;
            auto cmp = [](const auto &a, const auto &b) -> bool
            {
                return a.second < b.second;
            };
            int k = min_element(hash.begin(), hash.end(), cmp)->second;
            int ans = 0;
            for (;; k--)
            {
                for (auto &[num, freq] : hash)
                {
                    int q = freq / k, r = freq % k;
                    if (q < r)
                    {
                        ans = 0;
                        break;
                    }
                    ans += ceil((double)freq / (k + 1));
                }
                if (ans)
                    break;
            }
            return ans;
        }
    };
    // @lc code=end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    复杂度分析

    时间复杂度:O(n),其中 n 是数组 nums 的长度。设哈希表的大小为 m,哈希表中最小的 value 为 k,由于所有 value 之和为 n,所以 k * m ≤ n 。而循环次数又至多为 k * m,所以时间复杂度为 O(n)。

    空间复杂度:O(n),其中 n 是数组 nums 的长度。

    题目4:2911. 得到 K 个半回文串的最少修改次数

    思路

    题解:预处理+记忆化搜索/递推(附题单!)Python/Java/C++/Go

    在这里插入图片描述

    代码

    /*
     * @lc app=leetcode.cn id=2911 lang=cpp
     *
     * [2911] 得到 K 个半回文串的最少修改次数
     */
    
    // @lc code=start
    
    // 预处理每个数的真因子,时间复杂度 O(MX * log(MX))
    const int MX = 201;
    vector<vector<int>> divisors(MX);
    int init = []
    {
        for (int i = 1; i < MX; i++)
            for (int j = i * 2; j < MX; j += i)
                divisors[j].push_back(i);
        return 0;
    }();
    
    class Solution
    {
    public:
        int minimumChanges(string s, int k)
        {
            int n = s.size();
            vector<vector<int>> modify(n - 1, vector<int>(n));
            for (int left = 0; left < n - 1; left++)
                for (int right = left + 1; right < n; right++)
                {
                    string subStr = s.substr(left, right - left + 1);
                    modify[left][right] = get_modify(subStr);
                }
            vector<vector<int>> memo(k, vector<int>(n, n + 1)); // n+1 表示没有计算过
            function<int(int, int)> dfs = [&](int i, int j) -> int
            {
                if (i == 0)
                    return modify[0][j];
                int &res = memo[i][j]; // 注意这里是引用
                if (res <= n)
                { // 之前计算过
                    return res;
                }
                for (int L = i * 2; L < j; L++)
                    res = min(res, dfs(i - 1, L - 1) + modify[L][j]);
                return res;
            };
            return dfs(k - 1, n - 1);
        }
        // 辅函数 - 计算字符串 s 变成半回文串需要修改的字符数目
        int get_modify(string s)
        {
            int n = s.size(), res = n;
            for (int d : divisors[n])
            {
                int cnt = 0;
                for (int i0 = 0; i0 < d; i0++)
                    for (int i = i0, j = n - d + i0; i < j; i += d, j -= d)
                        cnt += s[i] != s[j];
                res = min(res, cnt);
            }
            return res;
        }
    };
    // @lc code=end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    复杂度分析

    时间复杂度:O(n3logn),其中 n 是字符串 s 的长度。时间主要在预处理上,有 O(n2) 个子串,平均每个子串有 O(logn) 个因子,每个因子需要 O(n) 的时间计算修改次数。

    空间复杂度:O(n2),其中 n 是字符串 s 的长度。

  • 相关阅读:
    【开发经验】如何快速接入第三方接口
    基于openssl和国密算法生成CA、服务器和客户端证书
    探讨Socks5代理IP在跨境电商与网络游戏中的网络安全应用
    系列八、四大垃圾算法pk
    【MATLAB源码-第43期】基于matlab的turbo码误码率仿真比较不同迭代次数,采用logmap/sova算法。
    有向无权图的最短路径
    【答辩问题】计算机专业本科毕业设计答辩技巧
    devops|中小公司不要做研发效能度量
    新手想开一个传奇该如何操作?开一个传奇必须掌握哪些知识要点
    Debian10离线安装docker-20.10.13
  • 原文地址:https://blog.csdn.net/ProgramNovice/article/details/134264813