Leetcode 第 368 场周赛题解

Leetcode 第 368 场周赛题解

  • Leetcode 第 368 场周赛题解
    • 题目1:2908. 元素和最小的山形三元组 I
      • 思路
      • 代码
      • 复杂度分析
    • 题目2:2909. 元素和最小的山形三元组 II
      • 思路
      • 代码
      • 复杂度分析
    • 题目3:2910. 合法分组的最少组数
      • 思路
      • 代码
      • 复杂度分析
    • 题目4:2911. 得到 K 个半回文串的最少修改次数
      • 思路
      • 代码
      • 复杂度分析

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

复杂度分析

时间复杂度: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

复杂度分析

时间复杂度: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

复杂度分析

时间复杂度: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

Leetcode 第 368 场周赛题解_第1张图片

代码

/*
 * @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

复杂度分析

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

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

你可能感兴趣的:(Every,day,a,leetcode,leetcode,算法)