1. 最高频元素的频数 [滑动窗口/前缀和]
元素的 频数 是该元素在一个数组中出现的次数。
给你一个整数数组 nums
和一个整数 k
。在一步操作中,你可以选择 nums
的一个下标,并将该下标对应元素的值增加 1
。
执行最多 k
次操作后,返回数组中最高频元素的 最大可能频数 。
Example 1
输入:nums = [1,2,4], k = 5
输出:3
解释:对第一个元素执行 3 次递增操作,对第二个元素执 2 次递增操作,此时 nums = [4,4,4] 。
4 是数组中最高频元素,频数是 3 。
Example 2
输入:nums = [1,4,8,13], k = 5
输出:2
解释:存在多种最优解决方案:
- 对第一个元素执行 3 次递增操作,此时 nums = [4,4,8,13] 。4 是数组中最高频元素,频数是 2 。
- 对第二个元素执行 4 次递增操作,此时 nums = [1,8,8,13] 。8 是数组中最高频元素,频数是 2 。
- 对第三个元素执行 5 次递增操作,此时 nums = [1,4,13,13] 。13 是数组中最高频元素,频数是 2 。
代码 1
时间复杂度 O ( n log n ) O(n\log n) O(nlogn)
using ull = unsigned long long;
int maxFrequency(vector<int> &nums, int k) {
sort(nums.begin(), nums.end());
vector<ull> sum(nums.size() + 1, 0);
for (int i = 1; i <= nums.size(); ++i) sum[i] = sum[i - 1] + nums[i - 1];
int result = 0;
for (int i = 0, j = 1; j <= nums.size(); ++j) {
while (sum[j] - sum[i] + k < ull(nums[j - 1]) * (j - i)) i++;
result = max(result, j - i);
}
return result;
}
代码 2
时间复杂度
O
(
n
log
n
)
O(n\log n)
O(nlogn),对nums
排序时间复杂度
O
(
n
log
n
)
O(n\log n)
O(nlogn),其余遍历时间复杂度均为
O
(
n
)
O(n)
O(n)。代码2与代码1相同,在原数组中相同元素较多的情况下可以提高效率
using uint = unsigned int;
int maxFrequency(const vector<int> &numsInput, int k) {
unordered_map<uint, uint> mp;
mp[0] = 0;
for (int n:numsInput) ++mp[n];
vector<pair<uint, uint>> nums(mp.begin(), mp.end());
sort(nums.begin(), nums.end(), [](const pair<uint, uint> &lo, const pair<uint, uint> &hi) { return lo.first < hi.first; });
vector<uint> prevSum(mp.size()), prevCnt(mp.size());
for (uint i = 0, sum = 0, cnt = 0; i < nums.size(); ++i) {
sum += nums[i].first * nums[i].second;
prevSum[i] = sum;
cnt += nums[i].second;
prevCnt[i] = cnt;
}
uint iFast = 0, iSlow = 0;
while (iFast < nums.size() && prevSum[iFast] + k >= nums[iFast].first * prevCnt[iFast]) ++iFast;
uint result = prevCnt[--iFast];
while (++iFast < nums.size()) {
while (prevSum[iFast] - prevSum[iSlow] + k < (prevCnt[iFast] - prevCnt[iSlow]) * nums[iFast].first) ++iSlow;
uint frequency = (prevCnt[iFast] - prevCnt[iSlow]) + ((prevSum[iFast] - prevSum[iSlow] + k) - (prevCnt[iFast] - prevCnt[iSlow]) * nums[iFast].first) / (nums[iFast].first - nums[iSlow].first);
result = max(result, frequency);
}
return result;
}
代码 3 [官方题解]
时间复杂度 O ( n log n ) O(n\log n) O(nlogn)
class Solution {
public:
int maxFrequency(vector<int> &nums, int k) {
sort(nums.begin(), nums.end());
int n = nums.size();
long long total = 0;
int l = 0, res = 1;
for (int r = 1; r < n; ++r) {
total += (long long) (nums[r] - nums[r - 1]) * (r - l);
while (total > k) {
total -= nums[r] - nums[l];
++l;
}
res = max(res, r - l + 1);
}
return res;
}
};
2. 使数组和能被 P 整除 [前缀和]
给你一个正整数数组 nums
,请你移除 最短 子数组(可以为 空),使得剩余元素的 和 能被 p
整除。 不允许 将整个数组都移除。
请你返回你需要移除的最短子数组的长度,如果无法满足题目要求,返回 -1
。
子数组 定义为原数组中连续的一组元素。
Example 1
输入:nums = [3,1,4,2], p = 6
输出:1
解释:nums 中元素和为 10,不能被 p 整除。我们可以移除子数组 [4] ,剩余元素的和为 6 。
Example 2
输入:nums = [6,3,5,2], p = 9
输出:2
解释:我们无法移除任何一个元素使得和被 9 整除,最优方案是移除子数组 [5,2] ,剩余元素为 [6,3],和为 9 。
代码
int minSubarray(const vector<int> &nums, int p) {
vector<int> sum(nums.size() + 1, 0);
for (int i = 1; i <= nums.size(); ++i) sum[i] = (sum[i - 1] + nums[i - 1]) % p;
if (sum.back() == 0) return 0;
unordered_map<int, int> mp;
int result = INT_MAX;
for (int i = 0, target; i <= nums.size(); ++i) {
target = (sum[i] + p - sum.back()) % p;
if (mp.count(target)) result = min(result, i - mp[target]);
mp[sum[i]] = i;
}
return result == nums.size() ? -1 : result;
}
3. 所有排列中的最大和 [差分数组]
We have an array of integers, nums
, and an array of requests
where requests[i] = [starti, endi]
. The ith
request asks for the sum of nums[starti] + nums[starti + 1] + ... + nums[endi - 1] + nums[endi]
. Both starti
and endi
are 0-indexed.
Return the maximum total sum of all requests among all permutations of nums
.
Since the answer may be too large, return it modulo 10^9 + 7
.
Example 1
Input: nums = [1,2,3,4,5], requests = [[1,3],[0,1]]
Output: 19
Explanation: One permutation of nums is [2,1,3,4,5] with the following result:
requests[0] -> nums[1] + nums[2] + nums[3] = 1 + 3 + 4 = 8
requests[1] -> nums[0] + nums[1] = 2 + 1 = 3
Total sum: 8 + 3 = 11.
A permutation with a higher total sum is [3,5,4,2,1] with the following result:
requests[0] -> nums[1] + nums[2] + nums[3] = 5 + 4 + 2 = 11
requests[1] -> nums[0] + nums[1] = 3 + 5 = 8
Total sum: 11 + 8 = 19, which is the best that you can do.
Example 2
Input: nums = [1,2,3,4,5,6], requests = [[0,1]]
Output: 11
Explanation: A permutation with the max total sum is [6,5,4,3,2,1] with request sums [11].
代码
constexpr int mod = 1000000007;
using ull = unsigned long long;
int maxSumRangeQuery(vector<int> &nums, vector<vector<int>> &requests) {
int n = nums.size();
vector<int> diff(n + 1); // 差分数组
for (auto &interval :requests) {
int l = interval[0], r = interval[1];
if (l > n) continue; // 无意义区间
if (r > n) r = n; // 删除无意义无间
++diff[l], --diff[r + 1];
}
vector<tuple<ull, int>> intervalCount; // count, length
intervalCount.reserve(nums.size());
int prev = 0, idx = 0, sum = diff[0];
while (++idx < diff.size()) {
if (diff[idx] != 0) {
intervalCount.emplace_back(sum, idx - prev);
prev = idx;
sum += diff[idx];
}
}
sort(intervalCount.begin(), intervalCount.end(), greater<>());
sort(nums.begin(), nums.end(), greater<>());
idx = 0;
ull total = 0;
for (auto[count, length]:intervalCount) {
while (length--) {
total += count * nums[idx++];
total %= mod;
}
}
return total;
}
4. 将数组分成三个子数组的方案数 [前缀和]
A split of an integer array is good if:
- The array is split into three non-empty contiguous subarrays - named
left
,mid
,right
respectively from left to right. - The sum of the elements in
left
is less than or equal to the sum of the elements inmid
, and the sum of the elements inmid
is less than or equal to the sum of the elements inright
.
Given nums
, an array of non-negative integers, return the number of good ways to split nums
. As the number may be too large, return it modulo 10^9 + 7
.
Example 1
Input: nums = [1,1,1]
Output: 1
Explanation: The only good way to split nums is [1] [1] [1].
Example 2
Input: nums = [1,2,2,2,5,0]
Output: 3
Explanation: There are three good ways of splitting nums:
[1] [2] [2,2,5,0]
[1] [2,2] [2,5,0]
[1,2] [2,2] [5,0]
代码 [前缀和]
int waysToSplit(const vector<int> &nums) {
int n = nums.size();
vector<int> sum(n, 0);
for (int i = 0, s = 0; i < n; ++i) sum[i] = s = s + nums[i];
int result = 0;
for (int i = 0, left, mid, right; i < n - 2; ++i) {
left = sum[i];
if (left > sum.back() / 3) break; // 剪枝
for (int j = i + 1; j < n - 1; ++j) {
mid = sum[j] - sum[i];
right = sum[n - 1] - sum[j];
if (mid > right) break; // 剪枝
if (left <= mid && mid <= right) ++result;
}
}
return result;
}
代码 [前缀和/二分]
int waysToSplit(const vector<int> &nums) {
int n = nums.size(), ans = 0, mod = 1e9 + 7;
vector<int> sum(n + 1);
for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + nums[i - 1];
auto search_left = [&](int lo, int hi, int left) -> int {
while (lo < hi) {
int mi = lo + ((hi - lo) >> 1);
sum[mi] - left < left ? lo = mi + 1 : hi = mi;
}
return lo;
};
auto search_right = [&](int lo, int hi, int left) -> int {
while (lo < hi) {
int mi = lo + ((hi - lo) >> 1);
sum[mi] - left <= sum[n] - sum[mi] ? lo = mi + 1 : hi = mi;
}
return lo;
};
for (int i = 1, l, r; i <= n - 2; i++) {
l = search_left(i + 1, n, sum[i]);
r = search_right(l, n, sum[i]);
ans = (ans + r - l) % mod;
}
return ans;
}
代码 [zhangbw]
int waysToSplit(vector<int> &nums) {
int n = nums.size(), ans = 0, mod = 1e9 + 7;
vector<int> preSum(n + 1); // 存放前缀和
for (int i = 1; i <= n; i++) preSum[i] = preSum[i - 1] + nums[i - 1];
for (int i = 1, l = 0, r = 0; i <= n - 2; i++) { // i为left测的元素个数
l = max(i + 1, l);
while (l < n && preSum[l] - preSum[i] < preSum[i]) l++;
r = max(l, r);
while (r < n && preSum[r] - preSum[i] <= preSum[n] - preSum[r]) r++;
ans = (ans + r - l) % mod;
}
return ans;
}