题单来自灵神,这里针对我的刷题过程做一个记录,本系列的目的是记录心得+做题记录的统计
2.1 越短越合法/求最长/最大
3. 无重复字符的最长子串
题意: 给一个字符串s,找出不含重复字符的最长子串长度。
code
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int n = s.size();
unordered_map<char, int> cnt;
int l = 0, r = 0, res = 0;
for (r = 0; r < n; r++) {
cnt[s[r]]++;
if (cnt[s[r]] < 2) {
res = max(res, r - l + 1);
} else {
while (cnt[s[r]] > 1)
cnt[s[l++]]--;
}
}
return res;
}
};
simple code
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int n = s.size();
unordered_map<char, int> cnt;
int l = 0, r = 0, res = 0;
for (r = 0; r < n; r++) {
cnt[s[r]]++;
while (cnt[s[r]] > 1)
cnt[s[l++]]--;
res = max(res, r - l + 1);
}
return res;
}
};
3090. 每个字符最多出现两次的最长子字符串 1329
题意:给一个字符串s, 找出满足每个字符最多出现两次的最长子字符串,返回长度。
分析: 和第一题一样。
code
class Solution {
public:
int maximumLengthSubstring(string s) {
int n = s.size(), res = 0;
unordered_map<char, int> cnt;
int l = 0, r = 0;
for (int r = 0; r < n; r++) {
cnt[s[r]]++;
while (cnt[s[r]] > 2)
cnt[s[l++]]--;
res = max(res, r - l + 1);
}
return res;
}
};
1493. 删掉一个元素以后全为 1 的最长子数组 1423
题意:给一个二进制数组nums, 要删一个元素。删掉元素的结果数组中,返回最长的且 只包含1 的非空子数组长度。 不存在返回0
分析:删一个后只包含1, 也就是说窗口内最多一个0, 求最长窗口大小。(返回窗口大小 - 1)
code
class Solution {
public:
int longestSubarray(vector<int>& nums) {
int res = 0, n = nums.size();
int l = 0, r = 0, zeroCnt = 0;
for (int r = 0; r < n; r++) {
if (nums[r] == 0)
zeroCnt++;
while (zeroCnt > 1) {
if (nums[l] == 0)
zeroCnt--;
l++;
}
res = max(res, r - l + 1);
}
return res-1;
}
};
3634. 使数组平衡的最少移除数目 1453
题意:给一个正数组nums, 和一个整数k. 如果数组最大值至多 是最小值的k 倍, 则该数组被称为是平衡的。
可以从nums 中移除任意数量的元素,但是不能空数组。 为了让数组平衡, 需移除的最小数量。
分析: 数组排序,要么删最大,要么删最小。等价于在数组中找一个中间的区间保留,要求该区间是符合条件的。
code
class Solution {
public:
int minRemoval(vector<int>& nums, int k) {
int n = nums.size(), res = 1;
sort(nums.begin(), nums.end());
int l = 0, r = 0, cur = nums[0];
for (int r = 1; r < n; r++) {
while ((long long)nums[r] > (long long)cur * k) {
cur = nums[++l];
}
res = max(res, r - l + 1);
}
return n - res;
}
};
1208. 尽可能使字符串相等 1497
题意:两个长度相同的串 s ,t。将 s 中第 i 个字符编导t中第 i个字符,需要 |s[i]-t[i]|的开销。用于变更最大预算是 maxCost。 如果可以把s的子字符串转化成t的子字符串,返回可以转化的最大长度。 没有转化为0 。
分析:计算cost数组,然后找到最长的 满足 预算内的 区间长度。
code
class Solution {
public:
int equalSubstring(string s, string t, int maxCost) {
int n = s.size(), res = 0;
vector<int> cost(n, 0);
for (int i = 0; i < n; i++)
cost[i] = abs(s[i] - t[i]);
int l = 0, r = 0;
for (int r = 0; r < n; r++) {
maxCost -= cost[r];
while (maxCost < 0)
maxCost += cost[l++];
res = max(res, r - l + 1);
}
return res;
}
};
904. 水果成篮 1516
题意:fruits数组表示果树, 一排果树的种类。 采摘水果,要求:
- 只有两个篮子,每个篮子只能装一种水果,装的数量没有限制。
- 可以选任意一棵开始采摘,必须从每棵树恰好摘一个,应该符合篮子中水果类型。每摘一次向右移动一次。不符合就停止。 求最大水果数目
分析: 模版题。
code
class Solution {
public:
int totalFruit(vector<int>& fruits) {
int n = fruits.size();
int res = 0, diffCnt = 0;
unordered_map<int, int> cnt;
for (int l = 0, r = 0; r < n; r++) {
cnt[fruits[r]]++;
if (cnt[fruits[r]] == 1)
diffCnt++;
while (diffCnt > 2) {
cnt[fruits[l]]--;
if (cnt[fruits[l]] == 0)
diffCnt--;
l++;
}
res = max(res, r - l + 1);
}
return res;
}
};
1695. 删除子数组的最大得分 1529
题意:给一个整数数组nums,要删除一个含有 若干 不同元素 的子数组,删除的得分就是子数组元素和,返回只删一个子数组的最大得分
分析: 模版题
code
class Solution {
public:
int maximumUniqueSubarray(vector<int>& nums) {
int n = nums.size(), res = 0, sum = 0;
unordered_map<int, int> cnt;
for (int l = 0, r = 0; r < n; r++) {
cnt[nums[r]]++;
sum += nums[r];
while (cnt[nums[r]] > 1) {
cnt[nums[l]]--;
sum -= nums[l];
l++;
}
res = max(res,sum);
}
return res;
}
};
2958. 最多 K 个重复元素的最长子数组 1535
题意:给一个整数数组nums, 和一个整数k。
一个元素x 在数组中的频率指出现次数。求频率小于k的最长子数组。
code
class Solution {
public:
int maxSubarrayLength(vector<int>& nums, int k) {
int n = nums.size();
unordered_map<int, int> cnt;
int res = 0;
for (int l = 0, r = 0; r < n; r++) {
cnt[nums[r]]++;
while (cnt[nums[r]] > k) {
cnt[nums[l++]]--;
}
res = max(res, r - l + 1);
}
return res;
}
};
2024. 考试的最大困扰度 1643
题意:老师出n道题, 每道答案 'T' 或者 'F' ,想增加不确定性,最大化连续相同结果的题数。 给一个字符串answerkey, 表示问题正确答案。还给一个k,每次可以将答案改成T 或者F, 返回不超过k次操作情况下, 最大连续T 或F数目。
分析: 一开始想的是T/F分情况两次滑窗,其实等价于 窗口内T/F都超过k 不行,其中一个不超过即可。 代码中判断TF的部分进一步可以根据ASCII码奇偶不同进一步优化。
code
class Solution {
public:
int maxConsecutiveAnswers(string answerKey, int k) {
int n = answerKey.size();
int res = 0;
vector<int> cnt(2, 0);
for (int l = 0, r = 0; r < n; r++) {
if (answerKey[r] == 'T')
cnt[0]++;
else
cnt[1]++;
while (cnt[0] > k && cnt[1] > k) {
if (answerKey[l] == 'T')
cnt[0]--;
else
cnt[1]--;
l++;
}
res = max(res, r - l + 1);
}
return res;
}
};
1004. 最大连续 1 的个数 III 1656
题意: 给一个二进制数组nums 和一个整数k。假设最多可以反转k个0,返回执行操作后数组中连续1的最大个数。
分析: 找最长区间,里面0个数小于等于k
code
class Solution {
public:
int longestOnes(vector<int>& nums, int k) {
int n = nums.size(), res = 0;
for (int l = 0, r = 0; r < n; r++) {
if (nums[r] == 0)
k--;
while (k < 0) {
if (nums[l] == 0)
k++;
l++;
}
res = max(res, r - l + 1);
}
return res;
}
};
2.1.2 进阶
上面的基础做起来还是比较快的。感觉比定长的后面几题做起来舒服多了。
2730. 找到最长的半重复子字符串 非暴力做法
题意:给一个字符串s, 只包含0-9字符串。 如果一个字符串t中至多有一对相邻字符是相等的,称这个串t是半重复的。例如0010, 002020. 2002, 54944返回s中最长半重复的子字符串长度。
code
class Solution {
public:
int longestSemiRepetitiveSubstring(string s) {
int n = s.size(), res = 1;
int same = 0;
for (int l = 0, r = 0; r < n; r++) {
if (r > 0 && s[r] == s[r - 1])
same++;
if (same > 1) {
l++;
while (s[l] != s[l - 1]) {
l++;
}
same = 1;
}
res = max(res, r - l + 1);
}
return res;
}
};
2779. 数组的最大美丽值 1638
题意:给一个下标从0开始的整数数组nums, 非负整数k。在一步操作中可以执行下述指令:
- 选一个之前没选过的下标i。
- 把nums[i]换成 nums[i]-k , nums[i] +k 的任一整数。
数组美丽🈯值定义为数组中相等元素组成的最长子序列长度,
对数组执行上述操作任意次后,返回最大美丽值。
每个下标只能执行一次。
分析: 每个数字有一个变化区间, 求所有区间交集最多的那个数字是多少。哦哦先排序, 然后滑窗。
code
class Solution {
public:
int maximumBeauty(vector<int>& nums, int k) {
int n = nums.size(), res = 0;
sort(nums.begin(), nums.end());
for (int l = 0, r = 0; r < n; r++) {
while(nums[r] - nums[l] > k * 2){
l++;
}
res = max(res, r-l+1);
}
return res;
}
};
1658. 将 x 减到 0 的最小操作数 1817
题意:给一个nums 和k , 每次可以移除最左边或最右边,然后从x减去。 如果可以将x恰好减到0, 返回最小操作数。
注意数据边界。
code
class Solution {
public:
int minOperations(vector<int>& nums, int x) {
int n = nums.size(), res = -1, sum = 0;
int total = reduce(nums.begin(), nums.end()); // total - x
if (total - x < 0)
return -1;
for (int l = 0, r = 0; r < n; r++) {
sum += nums[r];
while (sum > total - x) {
sum -= nums[l++];
}
if (sum + x == total)
res = max(res, r - l + 1);
}
cout << res << endl;
if (res == -1)
return -1;
return n - res;
}
};
1838. 最高频元素的频数 1876
题意:元素的频数是元素在数组中出现次数,给一个nums 和一个k , 每次可以次选择nums一个下标,把对应元素值加1. 执行最多k次操作后,返回最高频元素的最大可能频数。
分析: 排序后维护一个窗口,窗口中元素都变成最右端的target 元素,
code
class Solution {
public:
int maxFrequency(vector<int>& nums, int k) {
int n = nums.size();
sort(nums.begin(), nums.end());
long long sum = 0;
int res = 0;
for (int l = 0, r = 0; r < n; r++) {
sum += nums[r];
while (1LL* nums[r] * (r - l + 1) > sum + k) {
sum -= nums[l++];
}
res = max(res, r - l + 1);
}
return res;
}
};
2516. 每种字符至少取 K 个 1948
题意:给一个由字符 'a','b','c' 组成的字符串s 和一个非负整数k 。每分钟可以选择取走s最左侧还是最右侧的字符。 必须取走每种字符至少k个,返回需要的最少分钟数。
如果无法做到返回-1.
code
class Solution {
public:
int takeCharacters(string s, int k) {
int n = s.size();
vector<int> cnt(3, 0);
for (int i = 0; i < n; i++)
cnt[s[i] - 'a']++;
for (int i = 0; i < 3; i++) {
if (cnt[i] < k)
return -1;
cnt[i] -= k;
}
int sum = 0, res = 0;
for (int l = 0, r = 0; r < n; r++) {
cnt[s[r] - 'a']--;
while (cnt[s[r] - 'a'] < 0) {
cnt[s[l++] - 'a']++;
}
res = max(res, r - l + 1);
}
return n - res;
}
};
2831. 找出最长等值子数组 1976
题意:给一个nums 和整数 k ,如果所有元素相等,认为是一个等值子数组。 空数组是等值子数组。从nums 中删除最多k个元素后, 返回可能的最长等值子数组的长度。
分析:想了一会没见过这类题,看了灵神题解妙啊。 分组+滑窗。 尤其是 优化了 pos 数组存储内容 简化最终判断条件比较优雅。
code
class Solution {
public:
int longestEqualSubarray(vector<int>& nums, int k) {
int n = nums.size();
int res = 0;
vector<vector<int>> pos_list(n + 1);
for (int i = 0; i < n; i++) {
int x = nums[i];
pos_list[x].push_back(i - pos_list[x].size());
}
for (auto& pos : pos_list) {
int l = 0;
for (int r = 0; r < pos.size(); r++) {
while (pos[r] - pos[l] > k) {
l++;
}
res = max(res, r - l + 1);
}
}
return res;
}
};
浙公网安备 33010602011771号