mirror of
https://gitlab.com/game-loader/hugo.git
synced 2025-04-20 05:52:07 +08:00
leetcode update
This commit is contained in:
parent
14f215c188
commit
777dd97140
@ -18904,7 +18904,8 @@ Return true if it is possible to obtain the string target by moving the pieces o
|
|||||||
则可以同时用两个指针分别遍历target和start,每当在target中遇到一个非空字符时,若为'L'则移动start中的指针直到找到一个'L',若start中的'L'的下标和target中'L'下标相同或者更大(即start中的'L'在target中对应'L'的右侧,这样就可以通过左移移动到target中对应的'L')。对'R'同理,注意target中L R出现的顺序要与start中相同且start中的非空格字符满足上述的下标条件('L'在target右侧,'R'在target左侧)。如果全部满足条件则说明可以转换成target,否则不可以。
|
则可以同时用两个指针分别遍历target和start,每当在target中遇到一个非空字符时,若为'L'则移动start中的指针直到找到一个'L',若start中的'L'的下标和target中'L'下标相同或者更大(即start中的'L'在target中对应'L'的右侧,这样就可以通过左移移动到target中对应的'L')。对'R'同理,注意target中L R出现的顺序要与start中相同且start中的非空格字符满足上述的下标条件('L'在target右侧,'R'在target左侧)。如果全部满足条件则说明可以转换成target,否则不可以。
|
||||||
|
|
||||||
### 代码
|
### 代码
|
||||||
```cpp
|
|
||||||
|
```cpp
|
||||||
class Solution {
|
class Solution {
|
||||||
public:
|
public:
|
||||||
bool canChange(string start, string target) {
|
bool canChange(string start, string target) {
|
||||||
@ -18942,49 +18943,54 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
## day273 2024-12-06
|
|
||||||
### 2554. Maximum Number of Integers to Choose From a Range I
|
## day273 2024-12-06
|
||||||
|
|
||||||
|
### 2554. Maximum Number of Integers to Choose From a Range I
|
||||||
|
|
||||||
You are given an integer array banned and two integers n and maxSum. You are choosing some number of integers following the below rules:
|
You are given an integer array banned and two integers n and maxSum. You are choosing some number of integers following the below rules:
|
||||||
|
|
||||||
The chosen integers have to be in the range [1, n].
|
The chosen integers have to be in the range [1, n].
|
||||||
Each integer can be chosen at most once.
|
Each integer can be chosen at most once.
|
||||||
The chosen integers should not be in the array banned.
|
The chosen integers should not be in the array banned.
|
||||||
The sum of the chosen integers should not exceed maxSum.
|
The sum of the chosen integers should not exceed maxSum.
|
||||||
Return the maximum number of integers you can choose following the mentioned rules.
|
Return the maximum number of integers you can choose following the mentioned rules.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### 题解
|
### 题解
|
||||||
|
|
||||||
本题先将banned数组排序,再根据有序的banned数组中的数字和n的范围限制将banned数组中相邻两个数字中间的数字段的全部数字加和,与maxSum比较,小于maxSum则说明这些数字可以全部取得,加到计数总数中,否则使用二分法找到这个数字区间内满足n的范围限制且加和后和小于等于maxSum的最大数字,累加计数即得最终结果。
|
本题先将banned数组排序,再根据有序的banned数组中的数字和n的范围限制将banned数组中相邻两个数字中间的数字段的全部数字加和,与maxSum比较,小于maxSum则说明这些数字可以全部取得,加到计数总数中,否则使用二分法找到这个数字区间内满足n的范围限制且加和后和小于等于maxSum的最大数字,累加计数即得最终结果。
|
||||||
|
|
||||||
### 代码
|
### 代码
|
||||||
```cpp
|
|
||||||
|
```cpp
|
||||||
|
|
||||||
class Solution {
|
class Solution {
|
||||||
public:
|
public:
|
||||||
int maxCount(vector<int>& banned, int n, int maxSum) {
|
int maxCount(vector<int>& banned, int n, int maxSum) {
|
||||||
sort(banned.begin(), banned.end());
|
sort(banned.begin(), banned.end());
|
||||||
|
|
||||||
vector<int> nums;
|
vector<int> nums;
|
||||||
nums.push_back(0);
|
nums.push_back(0);
|
||||||
for (int x : banned) {
|
for (int x : banned) {
|
||||||
if (x <= n) nums.push_back(x);
|
if (x <= n) nums.push_back(x);
|
||||||
}
|
}
|
||||||
nums.push_back(n + 1);
|
nums.push_back(n + 1);
|
||||||
|
|
||||||
int ans = 0;
|
int ans = 0;
|
||||||
long long sum = 0;
|
long long sum = 0;
|
||||||
|
|
||||||
for (int i = 1; i < nums.size(); i++) {
|
for (int i = 1; i < nums.size(); i++) {
|
||||||
int left = nums[i-1] + 1;
|
int left = nums[i-1] + 1;
|
||||||
int right = nums[i] - 1;
|
int right = nums[i] - 1;
|
||||||
|
|
||||||
if (left > right || left > n) continue;
|
if (left > right || left > n) continue;
|
||||||
right = min(right, n);
|
right = min(right, n);
|
||||||
|
|
||||||
long long count = right - left + 1;
|
long long count = right - left + 1;
|
||||||
long long rangeSum = (left + right) * count / 2;
|
long long rangeSum = (left + right) * count / 2;
|
||||||
|
|
||||||
if (sum + rangeSum <= maxSum) {
|
if (sum + rangeSum <= maxSum) {
|
||||||
ans += count;
|
ans += count;
|
||||||
sum += rangeSum;
|
sum += rangeSum;
|
||||||
@ -18995,7 +19001,7 @@ public:
|
|||||||
int mid = low + (high - low) / 2;
|
int mid = low + (high - low) / 2;
|
||||||
count = mid - left + 1;
|
count = mid - left + 1;
|
||||||
rangeSum = (left + mid) * count / 2;
|
rangeSum = (left + mid) * count / 2;
|
||||||
|
|
||||||
if (sum + rangeSum <= maxSum) {
|
if (sum + rangeSum <= maxSum) {
|
||||||
low = mid + 1;
|
low = mid + 1;
|
||||||
} else {
|
} else {
|
||||||
@ -19007,16 +19013,19 @@ public:
|
|||||||
ans += count;
|
ans += count;
|
||||||
sum += (left + high) * count / 2;
|
sum += (left + high) * count / 2;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
## day274 2024-12-07
|
## day274 2024-12-07
|
||||||
### 1760. Minimum Limit of Balls in a Bag
|
|
||||||
|
### 1760. Minimum Limit of Balls in a Bag
|
||||||
|
|
||||||
You are given an integer array nums where the ith bag contains nums[i] balls. You are also given an integer maxOperations.
|
You are given an integer array nums where the ith bag contains nums[i] balls. You are also given an integer maxOperations.
|
||||||
|
|
||||||
You can perform the following operation at most maxOperations times:
|
You can perform the following operation at most maxOperations times:
|
||||||
@ -19025,11 +19034,12 @@ Take any bag of balls and divide it into two new bags with a positive number of
|
|||||||
For example, a bag of 5 balls can become two new bags of 1 and 4 balls, or two new bags of 2 and 3 balls.
|
For example, a bag of 5 balls can become two new bags of 1 and 4 balls, or two new bags of 2 and 3 balls.
|
||||||
Your penalty is the maximum number of balls in a bag. You want to minimize your penalty after the operations.
|
Your penalty is the maximum number of balls in a bag. You want to minimize your penalty after the operations.
|
||||||
|
|
||||||
Return the minimum possible penalty after performing the operations.
|
Return the minimum possible penalty after performing the operations.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### 题解
|
### 题解
|
||||||
|
|
||||||
本题起初想到计算出最终能分出的袋子的总数,将球的总数求和再按照尽可能均分的方式将球均分到袋子中,但这种思路在本题并不适用,因为均分后的最少的球的个数可能比当前某些袋子中已有的球的个数要多,而题目只允许将袋子中的球分开而不允许向袋子中添加球,因此这种情况是不符合题目要求的。我们只能通过模拟拆分的方式来模拟球的拆分过程最终得到每个袋子中球的个数。
|
本题起初想到计算出最终能分出的袋子的总数,将球的总数求和再按照尽可能均分的方式将球均分到袋子中,但这种思路在本题并不适用,因为均分后的最少的球的个数可能比当前某些袋子中已有的球的个数要多,而题目只允许将袋子中的球分开而不允许向袋子中添加球,因此这种情况是不符合题目要求的。我们只能通过模拟拆分的方式来模拟球的拆分过程最终得到每个袋子中球的个数。
|
||||||
|
|
||||||
既然只能通过模拟拆分方式来得到最终每个袋子中球的分配,那么必须要对拆分过程进行一个限制,这里我们可以限制允许拆分出来的每个袋子中球的最大个数,可以从具有最多球的袋子中球的个数减一开始,依次减一作为最大个数限制并模拟拆分过程来判断最终能否在满足我们自己定义的限制条件下拆分成功。如果成功,则可继续减小限制,不成功则不能继续减小限制。
|
既然只能通过模拟拆分方式来得到最终每个袋子中球的分配,那么必须要对拆分过程进行一个限制,这里我们可以限制允许拆分出来的每个袋子中球的最大个数,可以从具有最多球的袋子中球的个数减一开始,依次减一作为最大个数限制并模拟拆分过程来判断最终能否在满足我们自己定义的限制条件下拆分成功。如果成功,则可继续减小限制,不成功则不能继续减小限制。
|
||||||
@ -19037,7 +19047,8 @@ Return the minimum possible penalty after performing the operations.
|
|||||||
一个一个的减少个数限制效率比较低,此时发现其实“能否在最大个数限制下成功拆分”是一个二元条件,具有一个临界值,即比该临界个数大的个数限制必定都可以成功拆分,而小于该临界个数的个数限制必定不能成功拆分。因此可以使用二分法找到这个临界限制,这个临界限制就是我们最终要求的最小可能惩罚。
|
一个一个的减少个数限制效率比较低,此时发现其实“能否在最大个数限制下成功拆分”是一个二元条件,具有一个临界值,即比该临界个数大的个数限制必定都可以成功拆分,而小于该临界个数的个数限制必定不能成功拆分。因此可以使用二分法找到这个临界限制,这个临界限制就是我们最终要求的最小可能惩罚。
|
||||||
|
|
||||||
### 代码
|
### 代码
|
||||||
```cpp
|
|
||||||
|
```cpp
|
||||||
|
|
||||||
class Solution {
|
class Solution {
|
||||||
public:
|
public:
|
||||||
@ -19052,20 +19063,20 @@ public:
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int minimumSize(vector<int>& nums, int maxOperations) {
|
int minimumSize(vector<int>& nums, int maxOperations) {
|
||||||
int maxNum = 0;
|
int maxNum = 0;
|
||||||
for (int num : nums) {
|
for (int num : nums) {
|
||||||
maxNum = max(maxNum, num);
|
maxNum = max(maxNum, num);
|
||||||
}
|
}
|
||||||
|
|
||||||
int left = 1;
|
int left = 1;
|
||||||
int right = maxNum;
|
int right = maxNum;
|
||||||
int result = maxNum;
|
int result = maxNum;
|
||||||
|
|
||||||
while (left <= right) {
|
while (left <= right) {
|
||||||
int mid = left + (right - left) / 2;
|
int mid = left + (right - left) / 2;
|
||||||
|
|
||||||
if (canSplit(nums, maxOperations, mid)) {
|
if (canSplit(nums, maxOperations, mid)) {
|
||||||
result = mid;
|
result = mid;
|
||||||
right = mid - 1;
|
right = mid - 1;
|
||||||
@ -19073,7 +19084,686 @@ public:
|
|||||||
left = mid + 1;
|
left = mid + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## day275 2024-12-08
|
||||||
|
|
||||||
|
### 2054. Two Best Non-Overlapping Events
|
||||||
|
|
||||||
|
You are given a 0-indexed 2D integer array of events where events[i] = [startTimei, endTimei, valuei]. The ith event starts at startTimei and ends at endTimei, and if you attend this event, you will receive a value of valuei. You can choose at most two non-overlapping events to attend such that the sum of their values is maximized.
|
||||||
|
|
||||||
|
Return this maximum sum.
|
||||||
|
|
||||||
|
Note that the start time and end time is inclusive: that is, you cannot attend two events where one of them starts and the other ends at the same time. More specifically, if you attend an event with end time t, the next event must start at or after t + 1.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 题解
|
||||||
|
|
||||||
|
本题只能取两个互不重叠的事件并取二者的值的和,因此我们只需考虑某个事件a开始时间之前已经结束的所有事件中价值最大的事件,并将其和事件a的价值加和即为事件a与其前面可以共同取得的事件的价值和的最大值。
|
||||||
|
|
||||||
|
那么为什么不考虑事件a后面的事件呢,当遍历到后面的事件时,按照同样的方法与前面的事件的最大价值加和,此时若事件a不是这个最大值对应的事件,则事件a与后面事件的加和必然没有最大值对应事件与后面事件加和大,因此并不影响最终结果,换言之,我们将事件a和事件a后面发生的事件的和的大小问题推迟到处理后面的事件时一起处理,这样充分利用了前面已经处理过的事件的信息。
|
||||||
|
|
||||||
|
要实现前面所讲的思路,我们需要将事件按开始时间和结束时间分别排序,按开始时间遍历事件,确定事件的开始时间后遍历结束时间数组,找到所有开始时间之前结束的事件并更新这些事件中的价值的最大值,此处仅记录最大值即可,无需保存其他信息。随后将当前事件的价值和最大值加和并与全局最大值比较并更新全局最大值。
|
||||||
|
|
||||||
|
### 代码
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
int maxTwoEvents(vector<vector<int>>& events) {
|
||||||
|
int n = events.size();
|
||||||
|
// 创建两个数组分别存储按开始时间和结束时间排序的事件
|
||||||
|
vector<pair<int, int>> starts(n);
|
||||||
|
vector<pair<int, int>> ends(n);
|
||||||
|
|
||||||
|
for(int i = 0; i < n; i++) {
|
||||||
|
starts[i] = {events[i][0], i};
|
||||||
|
ends[i] = {events[i][1], i};
|
||||||
|
}
|
||||||
|
|
||||||
|
sort(starts.begin(), starts.end());
|
||||||
|
sort(ends.begin(), ends.end());
|
||||||
|
|
||||||
|
int maxValue = 0; // 记录已处理事件中的最大价值
|
||||||
|
int result = 0;
|
||||||
|
int endIndex = 0;
|
||||||
|
|
||||||
|
for(int i = 0; i < n; i++) {
|
||||||
|
int currentStart = starts[i].first;
|
||||||
|
int currentIndex = starts[i].second;
|
||||||
|
|
||||||
|
while(endIndex < n && ends[endIndex].first < currentStart) {
|
||||||
|
int idx = ends[endIndex].second;
|
||||||
|
maxValue = max(maxValue, events[idx][2]);
|
||||||
|
endIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = max(result, events[currentIndex][2] + maxValue);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## day276 2024-12-09
|
||||||
|
|
||||||
|
### 3152. Special Array II
|
||||||
|
|
||||||
|
An array is considered special if every pair of its adjacent elements contains two numbers with different parity.
|
||||||
|
|
||||||
|
You are given an array of integer nums and a 2D integer matrix queries, where for queries[i] = [fromi, toi] your task is to check that
|
||||||
|
subarray
|
||||||
|
nums[fromi..toi] is special or not.
|
||||||
|
|
||||||
|
Return an array of booleans answer such that answer[i] is true if nums[fromi..toi] is special.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 题解
|
||||||
|
|
||||||
|
本题若要query中的每个查询范围内都满足相邻元素具有不同的奇偶性,本题数字的奇偶性是有用的信息,数字本身的数值没什么用,故可以先遍历数组确定每个数字和其相邻数字的奇偶性的差异,我们用奇偶性差异值来表示这一差异,若下标i和下标i+1的数字的奇偶性不同,则称下标i处的奇偶性差异值为1,否则为0。在判断相邻数字奇偶性差异的同时,构造一个前缀和数组,保存从开头到每个下标处的子数组中奇偶性差异值的和。
|
||||||
|
|
||||||
|
对于一个query范围,若范围内的所有数字和其相邻数字的奇偶性均不同,则范围内的数字的奇偶性差异值的和应该和该范围的长度-1相等(每个奇偶差异值均为1),否则不同。求某个范围内的数字和是一个之前已经做过多次的题目,这种题目可以用前缀和求解,只需先求出确定了奇偶性的nums数组的全部下标的奇偶性差异前缀和,对每个query范围就可以快速确定范围内的子数组奇偶性差异和为多少。
|
||||||
|
|
||||||
|
### 代码
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
vector<bool> isArraySpecial(vector<int>& nums, vector<vector<int>>& queries) {
|
||||||
|
vector<int> prefix;
|
||||||
|
prefix.push_back(0);
|
||||||
|
int pre = 0;
|
||||||
|
for(int i=0;i<nums.size()-1;i++){
|
||||||
|
pre += (nums[i]%2)^(nums[i+1]%2);
|
||||||
|
prefix.push_back(pre);
|
||||||
|
}
|
||||||
|
vector<bool> result;
|
||||||
|
for(const auto& query : queries){
|
||||||
|
if(query[1]-query[0] == prefix[query[1]]-prefix[query[0]]){
|
||||||
|
result.push_back(true);
|
||||||
|
}else{
|
||||||
|
result.push_back(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## day277 2024-12-10
|
||||||
|
|
||||||
|
### 2981. Find Longest Special Substring That Occurs Thrice I
|
||||||
|
|
||||||
|
You are given a string s that consists of lowercase English letters.
|
||||||
|
|
||||||
|
A string is called special if it is made up of only a single character. For example, the string "abc" is not special, whereas the strings "ddd", "zz", and "f" are special.
|
||||||
|
|
||||||
|
Return the length of the longest special substring of s which occurs at least thrice, or -1 if no special substring occurs at least thrice.
|
||||||
|
|
||||||
|
A substring is a contiguous non-empty sequence of characters within a string.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 题解
|
||||||
|
|
||||||
|
本题若在遇到重复字符时直接统计字符的个数,由于相同字符的个数情况可能有很多,如当有四个重复字符时,其实包含了四个重复一次的字符,三个重复两次的字符,两个重复三次的字符及一个重复四次的字符,则每次遇到重复字符时,先将局部字符的个数全部记录,再利用一个哈希表,将每个重复个数的出现次数加入到哈希表对应的项中是可行的,但这样实际上需要将每个重复个数的字符串项都遍历一遍,如上面的例子,在有四个重复字符时,我们要在哈希表中分别更改1个,2个,3个,4个重复字符对应的项的值。
|
||||||
|
|
||||||
|
为了避免每次都要将所有重复字符的项都遍历一遍,可以想到如果我们先确定重复字符的个数,再去找这样的重复个数出现了几次就可以不用在意其他重复个数出现的情况了。如果这个重复个数的某个字符串在字符串中有三次及以上的出现次数,说明这个重复个数是可以取到的,否则不能取到,这种说法是不是感觉非常熟悉,没错这又是一个经典的二分法可以发挥作用的场景。存在一个临界值,使得临界值上成立,临界值下不成立,而要找的最大值也正是这个临界值。
|
||||||
|
|
||||||
|
使用二分法的左侧当然指向0,右边界可以先遍历数组,找到数组中相同字符最多重复了几次,以此作为右边界,再使用二分法,判断当前重复个数是否存在某个字符串出现了三次及以上(此处可使用滑动窗口,确定窗口内字符是否完全一样并用哈希表记录个数即可),最终找到刚好满足条件的临界个数即得结果。
|
||||||
|
|
||||||
|
### 代码
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
int maximumLength(string s) {
|
||||||
|
int maxrepeat = 0;
|
||||||
|
char cur = ' ';
|
||||||
|
int repeat = 0;
|
||||||
|
for(const auto& ch : s){
|
||||||
|
if (ch != cur){
|
||||||
|
maxrepeat = max(maxrepeat, repeat);
|
||||||
|
repeat = 1;
|
||||||
|
cur = ch;
|
||||||
|
}else{
|
||||||
|
repeat++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
maxrepeat = max(maxrepeat, repeat);
|
||||||
|
int left = 0;
|
||||||
|
while(left <= maxrepeat){
|
||||||
|
int mid = (left+maxrepeat)/2;
|
||||||
|
if(valid(s,mid)){
|
||||||
|
left = mid+1;
|
||||||
|
}else{
|
||||||
|
maxrepeat = mid-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(maxrepeat == 0){
|
||||||
|
return -1;
|
||||||
|
}else{
|
||||||
|
return maxrepeat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool valid(string s, int repeat){
|
||||||
|
vector<int> count(26,0);
|
||||||
|
for(int i=0;i<s.size();i++){
|
||||||
|
char cur = s[i];
|
||||||
|
bool flag = true;
|
||||||
|
for(int j=0;j<repeat;j++){
|
||||||
|
if(s[i+j] != s[i]){
|
||||||
|
flag = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(flag){
|
||||||
|
count[s[i]-'a']++;
|
||||||
|
if(count[s[i]-'a'] >= 3){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## day278 2024-12-11
|
||||||
|
|
||||||
|
### 2779. Maximum Beauty of an Array After Applying Operation
|
||||||
|
|
||||||
|
You are given a 0-indexed array nums and a non-negative integer k.
|
||||||
|
|
||||||
|
In one operation, you can do the following:
|
||||||
|
|
||||||
|
Choose an index i that hasn't been chosen before from the range [0, nums.length - 1].
|
||||||
|
Replace nums[i] with any integer from the range [nums[i] - k, nums[i] + k].
|
||||||
|
The beauty of the array is the length of the longest subsequence consisting of equal elements.
|
||||||
|
|
||||||
|
Return the maximum possible beauty of the array nums after applying the operation any number of times.
|
||||||
|
|
||||||
|
Note that you can apply the operation to each index only once.
|
||||||
|
|
||||||
|
A subsequence of an array is a new array generated from the original array by deleting some elements (possibly none) without changing the order of the remaining elements.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 题解
|
||||||
|
|
||||||
|
首先注意本题要找的是最长的子序列而不是子数组,因此不需要数字之间相邻,数字的先后顺序在本题中也不重要。考虑题目中允许的操作为将任意一个数字加上k或者减去k,且该操作只能执行一次。则设当前的数字为i,则在i-k和i+k范围内的全部数字均可通过题目所述操作变为相同数字。这个范围的大小是固定的2k,因此可以使用滑动窗口不断确定在这个范围内的所有数字的个数。
|
||||||
|
|
||||||
|
使用滑动窗口要有一个窗口的增长和收缩条件,我们知道要将窗口的范围始终限制在2k,则可先移动窗口左端的数字,使其变大,再根据左侧数字确定右侧的数字范围,移动窗口右侧的指针。这要求数组必须是有序的,有序情况下窗口左右才可以随着指针的移动数值自然变大。因此要先将数组排序,再使用上述滑动窗口方法即可得最终结果。
|
||||||
|
|
||||||
|
### 代码
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
int maximumBeauty(vector<int>& nums, int k) {
|
||||||
|
sort(nums.begin(),nums.end());
|
||||||
|
int left = 0;
|
||||||
|
int right = 0;
|
||||||
|
int n = nums.size();
|
||||||
|
int maxlen = 0;
|
||||||
|
while(left<n){
|
||||||
|
while(right<n && nums[right] <= nums[left]+2*k){
|
||||||
|
right++;
|
||||||
|
}
|
||||||
|
maxlen = max(maxlen, right-left);
|
||||||
|
while(left<n-1 && nums[left+1]==nums[left]){
|
||||||
|
left++;
|
||||||
|
}
|
||||||
|
left++;
|
||||||
|
}
|
||||||
|
return maxlen;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 总结
|
||||||
|
|
||||||
|
看了看最快的示例代码,发现了一个非常妙的思路,即可以直接通过设定每个nums中的值可以覆盖的数值范围,超出范围则减去相应的数字个数。这种方式可以直接通过不断加和的方式自动求得不同范围内的有效数字有多少个。
|
||||||
|
|
||||||
|
用这种方法要根据nums中的值来构造数组,数组的大小为nums中的最大值加上2\*k+1。这样构造的数组覆盖了nums中的全部值范围,上面说到的设定覆盖范围可以通过例子来理解,如当前数字为1,k的值为2,则可以直接将构造的新数组中下标为1处的值加1,而将1+2\*k+1,即6处的值减1。含义即为从1~1+2k范围内的数字都可以通过加减k的方式变成同一个数字,所以已有的1在这个范围内是一个有效的计数,但一旦离开这个范围,1就无法通过变换和其他数字变成同一个数了,因此1就不再是一个有效计数,因此可以减去数字1的计数。
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
int maximumBeauty(vector<int>& nums, int k) {
|
||||||
|
int m = *max_element(nums.begin(), nums.end()) + k * 2 + 2;
|
||||||
|
vector<int> d(m);
|
||||||
|
for (int x : nums) {
|
||||||
|
d[x]++;
|
||||||
|
d[x + k * 2 + 1]--;
|
||||||
|
}
|
||||||
|
int ans = 0, s = 0;
|
||||||
|
for (int x : d) {
|
||||||
|
s += x;
|
||||||
|
ans = max(ans, s);
|
||||||
|
}
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## day279 2024-12-12
|
||||||
|
|
||||||
|
### 2558. Take Gifts From the Richest Pile
|
||||||
|
|
||||||
|
You are given an integer array gifts denoting the number of gifts in various piles. Every second, you do the following:
|
||||||
|
|
||||||
|
Choose the pile with the maximum number of gifts.
|
||||||
|
If there is more than one pile with the maximum number of gifts, choose any.
|
||||||
|
Leave behind the floor of the square root of the number of gifts in the pile. Take the rest of the gifts.
|
||||||
|
Return the number of gifts remaining after k seconds.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 题解
|
||||||
|
|
||||||
|
本题是一道比较常规的模拟问题,对题目中每次都取所有礼物中的最大值自然想到这是一个最大堆的典型场景。先构建最大堆同时求出所有礼物个数的总和。使用最大堆记录礼物的数量,每次都取堆顶的个数,对其按照题目要求取平方根,计算原数字和平方根后取下整的数字的差并从总和中减去,再将新的取下整后的数字放入最大堆中。如此重复k次即得最终结果。
|
||||||
|
|
||||||
|
### 代码
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
long long pickGifts(vector<int>& gifts, int k) {
|
||||||
|
priority_queue<int> pq;
|
||||||
|
long long int sum = 0;
|
||||||
|
for(const auto& gift : gifts){
|
||||||
|
sum += gift;
|
||||||
|
pq.push(gift);
|
||||||
|
}
|
||||||
|
for(int i=0;i<k;i++){
|
||||||
|
int newgift = (int)floor(sqrt(pq.top()));
|
||||||
|
sum -= pq.top()-newgift;
|
||||||
|
pq.pop();
|
||||||
|
pq.push(newgift);
|
||||||
|
}
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## day280 2024-12-13
|
||||||
|
|
||||||
|
### 2593. Find Score of an Array After Marking All Elements
|
||||||
|
|
||||||
|
You are given an array nums consisting of positive integers.
|
||||||
|
|
||||||
|
Starting with score = 0, apply the following algorithm:
|
||||||
|
|
||||||
|
Choose the smallest integer of the array that is not marked. If there is a tie, choose the one with the smallest index.
|
||||||
|
Add the value of the chosen integer to score.
|
||||||
|
Mark the chosen element and its two adjacent elements if they exist.
|
||||||
|
Repeat until all the array elements are marked.
|
||||||
|
Return the score you get after applying the above algorithm.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 题解
|
||||||
|
|
||||||
|
本题每次都要选择未标记的数字中最小的那个,并将其相邻位置标记,可以构造一个布尔型标记数组来记录被标记的下标位置,该数组下标和nums中的下标对应。因为每次都选最小的,故先排序后直接遍历就能得到当前的最小数字,只需再判断其是否被标记即可。
|
||||||
|
|
||||||
|
对nums中的数字先构造结构体,将数字和对应的下标绑定,再使用稳定的排序方法对数字进行排序。按照上述算法不断遍历有序数组,用一个变量记录当前已经被标记的个数,当标记个数和数组长度相同时结束遍历并返回最终分数。
|
||||||
|
|
||||||
|
### 代码
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
long long findScore(vector<int>& nums) {
|
||||||
|
int n = nums.size();
|
||||||
|
vector<bool> mark(n,false);
|
||||||
|
vector<pair<int,int>> sorted;
|
||||||
|
for(int i=0;i<n;i++){
|
||||||
|
sorted.push_back({nums[i],i});
|
||||||
|
}
|
||||||
|
sort(sorted.begin(),sorted.end());
|
||||||
|
int marked = 0;
|
||||||
|
long long int score = 0;
|
||||||
|
for(const auto& sort_num : sorted){
|
||||||
|
if(!mark[sort_num.second]){
|
||||||
|
score += sort_num.first;
|
||||||
|
mark[sort_num.second] = true;
|
||||||
|
marked++;
|
||||||
|
if(sort_num.second > 0 && !mark[sort_num.second-1]){
|
||||||
|
mark[sort_num.second-1] = true;
|
||||||
|
marked++;
|
||||||
|
}
|
||||||
|
if(sort_num.second < n-1 && !mark[sort_num.second+1]){
|
||||||
|
mark[sort_num.second+1] = true;
|
||||||
|
marked++;
|
||||||
|
}
|
||||||
|
if(marked == n){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
## day281 2024-12-14
|
||||||
|
### 2762. Continuous Subarrays
|
||||||
|
You are given a 0-indexed integer array nums. A subarray of nums is called continuous if:
|
||||||
|
|
||||||
|
Let i, i + 1, ..., j be the indices in the subarray. Then, for each pair of indices i <= i1, i2 <= j, 0 <= |nums[i1] - nums[i2]| <= 2.
|
||||||
|
Return the total number of continuous subarrays.
|
||||||
|
|
||||||
|
A subarray is a contiguous non-empty sequence of elements within an array.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 题解
|
||||||
|
本题思考题目条件,要求子数组中任意两个数字之间差的绝对值不超过2。则可使用双指针来指示子数组当前的范围,同时使用两个变量分别记录当前子数组内的最大值和最小值。先将右指针向后移动,同时更新子数组内的最大值和最小值,判断二者的差是否小于等于2。等于则说明该子数组满足条件,要给结果加上子数组长度(相当于新加入的数字自身,加上数字和前面的各个子数组的组合)。
|
||||||
|
|
||||||
|
当新遍历的数使得数组内的最大值和最小值差大于2时,考虑两种情况,如上述情况数字范围为3~5,则若新数字为6,则之前的数组中若尾部仅包含4,5两个数字,4,5,6仍然符合题目要求的差的绝对值为2。故找到之前子数组的尾部仅包含4,5两个数字的部分保留。对于7同理。但若数字大于7,则之前的数组中不可能存在可以和7组合满足条件的数字,因此直接从7开始向后遍历即可。由此得出对于新数字使得子数组极差大于2时,若新数字和数组内的最大值或最小值差的绝对值不超过2,此时移动左指针直到子数组中的数字全部在新的给定范围内(具体实现上,只需超出范围的数字的个数为0即可)。否则直接从新数字开始向后遍历数组(将左右指针都设为该数字的位置)。
|
||||||
|
|
||||||
|
将每次新找到的满足条件的子数组长度加和即得最终结果。
|
||||||
|
|
||||||
|
|
||||||
|
### 代码
|
||||||
|
```cpp
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
long long continuousSubarrays(vector<int>& nums) {
|
||||||
|
long long int result = 0;
|
||||||
|
int left = 0;
|
||||||
|
int right = 0;
|
||||||
|
int n = nums.size();
|
||||||
|
unordered_map<int,int> count;
|
||||||
|
int current_min = nums[0];
|
||||||
|
int current_max = nums[0];
|
||||||
|
|
||||||
|
while (right < n) {
|
||||||
|
if (right > left &&
|
||||||
|
(abs(nums[right] - current_max) > 2 &&
|
||||||
|
abs(nums[right] - current_min) > 2)) {
|
||||||
|
count.clear();
|
||||||
|
left = right;
|
||||||
|
current_max = nums[right];
|
||||||
|
current_min = nums[right];
|
||||||
|
}
|
||||||
|
|
||||||
|
count[nums[right]]++;
|
||||||
|
|
||||||
|
current_max = max(current_max, nums[right]);
|
||||||
|
current_min = min(current_min, nums[right]);
|
||||||
|
|
||||||
|
while (current_max - current_min > 2) {
|
||||||
|
count[nums[left]]--;
|
||||||
|
if (count[nums[left]] == 0) {
|
||||||
|
count.erase(nums[left]);
|
||||||
|
}
|
||||||
|
left++;
|
||||||
|
|
||||||
|
current_max = nums[left];
|
||||||
|
current_min = nums[left];
|
||||||
|
for (auto& pair : count) {
|
||||||
|
if (pair.second > 0) {
|
||||||
|
current_max = max(current_max, pair.first);
|
||||||
|
current_min = min(current_min, pair.first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算当前窗口内的所有有效子数组数量
|
||||||
|
result += (right - left + 1);
|
||||||
|
right++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## day282 2024-12-15
|
||||||
|
### 1792. Maximum Average Pass Ratio
|
||||||
|
There is a school that has classes of students and each class will be having a final exam. You are given a 2D integer array classes, where classes[i] = [passi, totali]. You know beforehand that in the ith class, there are totali total students, but only passi number of students will pass the exam.
|
||||||
|
|
||||||
|
You are also given an integer extraStudents. There are another extraStudents brilliant students that are guaranteed to pass the exam of any class they are assigned to. You want to assign each of the extraStudents students to a class in a way that maximizes the average pass ratio across all the classes.
|
||||||
|
|
||||||
|
The pass ratio of a class is equal to the number of students of the class that will pass the exam divided by the total number of students of the class. The average pass ratio is the sum of pass ratios of all the classes divided by the number of the classes.
|
||||||
|
|
||||||
|
Return the maximum possible average pass ratio after assigning the extraStudents students. Answers within 10-5 of the actual answer will be accepted.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 题解
|
||||||
|
本题首先要了解一个分数自身的性质,对一个分数给分子分母同时加1,会使得分数的值向1靠近,意味着若分数小于1,则同时加1会使分数变大,分数大于1,同时加1会使分数变小。
|
||||||
|
|
||||||
|
本题学生的通过率都是小于等于1的,因此给通过率小于1的class分配一个通过的学生会使分子分母同时加1使得分数变大。本题要提高总体的平均通过率,需要使分配学生后通过率的增加幅度尽可能大,因此本题可使用最大堆,每次弹出在分子分母同时加1后通过率增加幅度最大的组合,给它分子分母同时加1并计算新的通过率增加幅度重新放入堆中。注意此处为了计算的精确性,需要保留通过率的浮点数和原始的分子分母的整数用于后续计算。
|
||||||
|
|
||||||
|
### 代码
|
||||||
|
```cpp
|
||||||
|
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
struct Class {
|
||||||
|
int pass;
|
||||||
|
int total;
|
||||||
|
double delta;
|
||||||
|
|
||||||
|
Class(int p, int t) {
|
||||||
|
pass = p;
|
||||||
|
total = t;
|
||||||
|
delta = (double)(p + 1) / (t + 1) - (double)p / t;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
double maxAverageRatio(vector<vector<int>>& classes, int extraStudents) {
|
||||||
|
// 按照delta降序排列的优先队列
|
||||||
|
auto comp = [](const Class& a, const Class& b) {
|
||||||
|
return a.delta < b.delta;
|
||||||
|
};
|
||||||
|
priority_queue<Class, vector<Class>, decltype(comp)> pq(comp);
|
||||||
|
|
||||||
|
for (const auto& c : classes) {
|
||||||
|
pq.push(Class(c[0], c[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
while (extraStudents--) {
|
||||||
|
Class curr = pq.top();
|
||||||
|
pq.pop();
|
||||||
|
|
||||||
|
curr.pass++;
|
||||||
|
curr.total++;
|
||||||
|
curr.delta = (double)(curr.pass + 1) / (curr.total + 1) - (double)curr.pass / curr.total;
|
||||||
|
|
||||||
|
pq.push(curr);
|
||||||
|
}
|
||||||
|
|
||||||
|
double sum = 0;
|
||||||
|
int n = classes.size();
|
||||||
|
while (!pq.empty()) {
|
||||||
|
const Class& c = pq.top();
|
||||||
|
sum += (double)c.pass / c.total;
|
||||||
|
pq.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum / n;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
## day283 2024-12-16
|
||||||
|
### 3264. Final Array State After K Multiplication Operations I
|
||||||
|
You are given an integer array nums, an integer k, and an integer multiplier.
|
||||||
|
|
||||||
|
You need to perform k operations on nums. In each operation:
|
||||||
|
|
||||||
|
Find the minimum value x in nums. If there are multiple occurrences of the minimum value, select the one that appears first.
|
||||||
|
Replace the selected minimum value x with x * multiplier.
|
||||||
|
Return an integer array denoting the final state of nums after performing all k operations.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 题解
|
||||||
|
本题使用最小堆可解。构造一个pair将数字和对应的下标存储起来,再将这些pair插入最小堆,此处注意定义最小堆的比较规则,当比较数值后,若数值相同还要比较下标。每次从最小堆中弹出顶部数字,按照下标将nums对应数字和multiplier相乘即可。
|
||||||
|
|
||||||
|
### 代码
|
||||||
|
```cpp
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
vector<int> getFinalState(vector<int>& nums, int k, int multiplier) {
|
||||||
|
auto cmp = [](const pair<int,int> a, const pair<int,int> b){
|
||||||
|
if(a.first == b.first){
|
||||||
|
return a.second > b.second;
|
||||||
|
}
|
||||||
|
return a.first>b.first;
|
||||||
|
};
|
||||||
|
priority_queue<pair<int,int>, vector<pair<int,int>>, decltype(cmp)> pq(cmp);
|
||||||
|
for(int i=0;i<nums.size();i++){
|
||||||
|
pq.push({nums[i],i});
|
||||||
|
}
|
||||||
|
while(k>0){
|
||||||
|
auto num = pq.top();
|
||||||
|
nums[num.second] *= multiplier;
|
||||||
|
num.first *= multiplier;
|
||||||
|
pq.pop();
|
||||||
|
pq.push(num);
|
||||||
|
k--;
|
||||||
|
}
|
||||||
|
return nums;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
## day284 2024-12-17
|
||||||
|
### 2182. Construct String With Repeat Limit
|
||||||
|
You are given a string s and an integer repeatLimit. Construct a new string repeatLimitedString using the characters of s such that no letter appears more than repeatLimit times in a row. You do not have to use all characters from s.
|
||||||
|
|
||||||
|
Return the lexicographically largest repeatLimitedString possible.
|
||||||
|
|
||||||
|
A string a is lexicographically larger than a string b if in the first position where a and b differ, string a has a letter that appears later in the alphabet than the corresponding letter in b. If the first min(a.length, b.length) characters do not differ, then the longer string is the lexicographically larger one.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 题解
|
||||||
|
本题先统计字符串s中各个字母的字数,再根据题意构造最大字符串,构造最大字符串需要将所有字符从最大字符开始排列,但排列时相同字符的连续重复次数不能超过repeatLimit。则每次当最大字符重复了repeatLimit后,需要插入一个次大字符,再继续插入最大字符,因此可以使用双指针,分别指示当前的最大字符和次大字符,再按照题目要求通过不断交替插入尽可能多的最大字符和用于分隔的次大字符来构造整个字符串。
|
||||||
|
|
||||||
|
这样得到的就是最大的字符串,对于剩余的多余字符,如剩下了很多a没有使用并不影响这个字符串的最大性,因为题目只要求使用s中的字符得到最大字符串,不要求将字符全部用完。
|
||||||
|
|
||||||
|
|
||||||
|
### 代码
|
||||||
|
```cpp
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
string repeatLimitedString(string s, int repeatLimit) {
|
||||||
|
int maxindex = 0;
|
||||||
|
int secondindex = -1;
|
||||||
|
vector<int> count(26,0);
|
||||||
|
for(const auto& ch : s){
|
||||||
|
count[ch-'a']++;
|
||||||
|
}
|
||||||
|
for(int i=25;i>=0;i--){
|
||||||
|
if(count[i] > 0){
|
||||||
|
maxindex = i;
|
||||||
|
for(int j=i-1;j>=0;j--){
|
||||||
|
if(count[j]>0){
|
||||||
|
secondindex = j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
string result;
|
||||||
|
int limit = repeatLimit;
|
||||||
|
while(maxindex >= 0){
|
||||||
|
while(count[maxindex] > 0 && limit > 0){
|
||||||
|
result.push_back('a' + maxindex);
|
||||||
|
count[maxindex]--;
|
||||||
|
limit--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(count[maxindex] > 0){
|
||||||
|
if(secondindex >= 0){
|
||||||
|
if(count[secondindex] > 0){
|
||||||
|
result.push_back('a' + secondindex);
|
||||||
|
count[secondindex]--;
|
||||||
|
limit = repeatLimit;
|
||||||
|
} else {
|
||||||
|
secondindex = -1;
|
||||||
|
for(int i = maxindex-1; i >= 0; i--){
|
||||||
|
if(count[i] > 0){
|
||||||
|
secondindex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(secondindex < 0) break;
|
||||||
|
result.push_back('a' + secondindex);
|
||||||
|
count[secondindex]--;
|
||||||
|
limit = repeatLimit;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
maxindex = secondindex;
|
||||||
|
secondindex = -1;
|
||||||
|
for(int i = maxindex-1; i >= 0; i--){
|
||||||
|
if(count[i] > 0){
|
||||||
|
secondindex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
limit = repeatLimit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
### 总结
|
||||||
|
这次代码写的有点丑陋了,但思路上是没问题的,可以参考下其他人写的思路基本一样的代码
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
string repeatLimitedString(string s, int repeatLimit) {
|
||||||
|
vector<int> freq(26, 0);
|
||||||
|
for (char ch : s) {
|
||||||
|
freq[ch - 'a']++;
|
||||||
|
}
|
||||||
|
|
||||||
|
string result;
|
||||||
|
int currentCharIndex = 25; // Start from the largest character
|
||||||
|
while (currentCharIndex >= 0) {
|
||||||
|
if (freq[currentCharIndex] == 0) {
|
||||||
|
currentCharIndex--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int use = min(freq[currentCharIndex], repeatLimit);
|
||||||
|
result.append(use, 'a' + currentCharIndex);
|
||||||
|
freq[currentCharIndex] -= use;
|
||||||
|
|
||||||
|
if (freq[currentCharIndex] >
|
||||||
|
0) { // Need to add a smaller character
|
||||||
|
int smallerCharIndex = currentCharIndex - 1;
|
||||||
|
while (smallerCharIndex >= 0 && freq[smallerCharIndex] == 0) {
|
||||||
|
smallerCharIndex--;
|
||||||
|
}
|
||||||
|
if (smallerCharIndex < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result.push_back('a' + smallerCharIndex);
|
||||||
|
freq[smallerCharIndex]--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user