Leetcode 第 412 场周赛题解
Leetcode 第 412 场周赛题解
题目1:3264. K 次乘运算后的最终数组 I
思路
操作:遍历数组 nums,找到其中的最小值 x,如果存在多个最小值,选择最前面的一个。将它乘以 multiplier。
共执行 k 次操作。
代码
/*
* @lc app=leetcode.cn id=3264 lang=cpp
*
* [3264] K 次乘运算后的最终数组 I
*/
// @lc code=start
class Solution
{
public:
vector<int> getFinalState(vector<int> &nums, int k, int multiplier)
{
int n = nums.size();
while (k--)
{
int min_idx = 0;
for (int i = 1; i < n; i++)
if (nums[i] < nums[min_idx])
min_idx = i;
nums[min_idx] *= multiplier;
}
return nums;
}
};
// @lc code=end
复杂度分析
时间复杂度:O(n * k),其中 n 是数组 nums 的长度。
空间复杂度:O(1)。
题目2:3265. 统计近似相等数对 I
思路
暴力枚举数组 nums 中下标 i 和 j 满足 i < j 的 nums[i] 和 nums[j],判断它们是否近似相等。
细节:先对数组 nums 升序排序,在判断它们是否近似相等,转成字符串进行比较,且只交换较大数的数位。
代码
/*
* @lc app=leetcode.cn id=3265 lang=cpp
*
* [3265] 统计近似相等数对 I
*/
// @lc code=start
class Solution
{
public:
int countPairs(vector<int> &nums)
{
int n = nums.size();
sort(nums.begin(), nums.end());
int ans = 0;
for (int i = 0; i < n - 1; i++)
for (int j = i + 1; j < n; j++)
if (check(to_string(nums[i]), to_string(nums[j])))
ans++;
return ans;
}
// 辅函数
bool check(string s1, string s2)
{
if (stoi(s1) == stoi(s2))
return true;
int len2 = s2.length();
for (int i = 0; i < len2 - 1; i++)
for (int j = i + 1; j < len2; j++)
{
swap(s2[i], s2[j]); // 交换数位
if (stoi(s1) == stoi(s2))
return true;
else
{
// 记得复原
swap(s2[i], s2[j]);
}
}
return false;
}
};
// @lc code=end
复杂度分析
时间复杂度:O(nlogn + n2 * len(max(nums))2),其中 n 是数组 nums 的长度。
空间复杂度:O(1)。
题目3:3266. K 次乘运算后的最终数组 II
思路
用最小堆手动模拟操作,直到原数组的最大值 mx 成为这 n 个数的最小值。
设此时还剩下 k 次操作,那么:
- 对于前 k mod n 小的数,还可以再操作 k/n+1 次。
- 其余元素,还可以再操作 k/n 次。
用快速幂计算操作这么多次后的结果。
代码
/*
* @lc app=leetcode.cn id=3266 lang=cpp
*
* [3266] K 次乘运算后的最终数组 II
*/
// @lc code=start
class Solution
{
private:
const int MOD = 1e9 + 7;
// 快速幂
long long pow(long long x, int n)
{
long long res = 1;
for (; n; n /= 2)
{
if (n % 2)
res = res * x % MOD;
x = x * x % MOD;
}
return res;
}
public:
vector<int> getFinalState(vector<int> &nums, int k, int multiplier)
{
// 特判
if (multiplier == 1)
return move(nums);
int n = nums.size();
int mx = *max_element(nums.begin(), nums.end());
vector<pair<long long, int>> h(n);
for (int i = 0; i < n; i++)
{
h[i] = {nums[i], i};
}
ranges::make_heap(h, greater<>()); // 最小堆,O(n) 堆化
// 模拟,直到堆顶是 mx
for (; k && h[0].first < mx; k--)
{
ranges::pop_heap(h, greater<>());
h.back().first *= multiplier;
ranges::push_heap(h, greater<>());
}
// 剩余的操作可以直接用公式计算
ranges::sort(h);
for (int i = 0; i < n; i++)
{
auto &[x, j] = h[i];
nums[j] = x % MOD * pow(multiplier, k / n + (i < k % n)) % MOD;
}
return move(nums);
}
};
// @lc code=end
复杂度分析
时间复杂度:O(n * logn * logmU),其中 n 是数组 nums 的长度,U=max(nums),m=multiplier。
空间复杂度:O(n),其中 n 是数组 nums 的长度。
题目4:3267. 统计近似相等数对 II
思路
按题目二的做法,再加二层循环做第二次交换,再比较:
/*
* @lc app=leetcode.cn id=3267 lang=cpp
*
* [3267] 统计近似相等数对 II
*/
// @lc code=start
class Solution
{
public:
int countPairs(vector<int> &nums)
{
int n = nums.size();
sort(nums.begin(), nums.end());
int ans = 0;
for (int i = 0; i < n - 1; i++)
for (int j = i + 1; j < n; j++)
if (check(to_string(nums[i]), to_string(nums[j])))
ans++;
return ans;
}
// 辅函数
bool check(string s1, string s2)
{
if (stoi(s1) == stoi(s2))
return true;
int len2 = s2.length();
for (int i = 0; i < len2 - 1; i++)
for (int j = i + 1; j < len2; j++)
{
swap(s2[i], s2[j]); // 交换数位
if (stoi(s1) == stoi(s2))
return true;
else
{
for (int p = i + 1; p < len2 - 1; p++)
for (int q = p + 1; q < len2; q++)
{
swap(s2[p], s2[q]); // 交换数位
if (stoi(s1) == stoi(s2))
return true;
else
{
// 记得复原
swap(s2[p], s2[q]);
}
}
// 记得复原
swap(s2[i], s2[j]);
}
}
return false;
}
};
// @lc code=end
这样做会超时:
把 nums 按照数字长度从小到大排序(也就是元素值从小到大排序),枚举 j 并暴力交换 nums[j] 中的数位。设交换后的数字为 x,统计左边有多少个 nums[i] 等于 x。
为避免重复统计,可以先用一个哈希集合记录交换后的数字,再去遍历哈希集合中的元素 x,统计左边有多少个 nums[i] 等于 x。
代码
/*
* @lc app=leetcode.cn id=3267 lang=cpp
*
* [3267] 统计近似相等数对 II
*/
// @lc code=start
class Solution
{
public:
int countPairs(vector<int> &nums)
{
int n = nums.size();
sort(nums.begin(), nums.end());
unordered_map<int, int> cnt;
int ans = 0;
for (int &x : nums)
{
unordered_set<int> st = {x}; // 不交换
string s = to_string(x);
int m = s.length();
for (int i = 0; i < m; i++)
{
for (int j = i + 1; j < m; j++)
{
swap(s[i], s[j]);
st.insert(stoi(s)); // 交换一次
for (int p = i + 1; p < m; p++)
{
for (int q = p + 1; q < m; q++)
{
swap(s[p], s[q]);
st.insert(stoi(s)); // 交换两次
swap(s[p], s[q]);
}
}
swap(s[i], s[j]);
}
}
for (int v : st)
{
ans += cnt.contains(v) ? cnt[v] : 0;
}
cnt[x]++;
}
return ans;
}
};
// @lc code=end
复杂度分析
时间复杂度:O(nlogn+nlog5U),其中 n 是数组 nums 的长度,U=max(nums)。
空间复杂度:O(n+nlog4U),其中 n 是数组 nums 的长度,U=max(nums)。