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; // <value, frequency>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
代码
/** @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 的长度。