题单来自灵神,这里针对我的刷题过程做一个记录,本系列的目的是记录心得+做题记录的统计
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;
}
};
2271. 毯子覆盖的最多白色砖块数 2022
题意:给一个二维数组tiles,其中 tiles[l_i, r_i]表示在l_i, r_i 之间的每个瓷砖位置j 都涂成了白色。
同时给一个整数 carpetLen , 表示可以放在任何位置的一块毯子的长度,返回使用这块毯子,最多可以盖住多少块白色瓷砖。
/
code
class Solution {
public:
int maximumWhiteTiles(vector<vector<int>>& tiles, int carpetLen) {
int n = tiles.size();
int res = 0;
ranges::sort(tiles, {}, [](auto& t) { return t[0]; });
int cover = 0, l = 0;
for (auto& tile : tiles) {
int tl = tile[0], tr = tile[1];
cover += tr - tl + 1;
int carpetLeft = tr - carpetLen + 1;
while (tiles[l][1] < carpetLeft) {
cover -= (tiles[l][1] - tiles[l][0] + 1);
l++;
}
int uncover = max(0, carpetLeft - tiles[l][0]);
res = max(res, cover - uncover);
}
return res;
}
};
2106. 摘水果 2062
题意:x坐标轴上有水果, 二维数组 fruits , fruits[i]= [position_i, amount_i], 表示posi上一共有 amount_i个水果。fruits已经按position升序。每个position 互不相同。
另外给两个整数 startPos 和 k, 最初位于startPos。 可以向左/向右,最多走k步,返回摘到最大水果数。
好像可以把人的范围确定出来。 l = startPos-k+1, r = startPos+k. 但是如果要两个方向都摘,那么需要最短的距离*2+ 长的距离。
不妨从l,r 维护所有水果是否要摘,每次向右摘不到的就放弃最左边的。 统计最大值即可。
/
code
class Solution {
public:
int maxTotalFruits(vector<vector<int>>& fruits, int startPos, int k) {
int n = fruits.size();
int min_l = max(0, startPos - k), max_r = startPos + k;
int l = 0, r = 0;
int res = 0, cur = 0;
while (l < n && fruits[l][0] < min_l)
l++;
r = l;
while (r < n && fruits[r][0] <= startPos) {
cur += fruits[r][1];
r++;
}
res = cur;
for (; r < n && fruits[r][0] <= max_r; r++) {
cur += fruits[r][1];
while (fruits[r][0] * 2 - startPos - fruits[l][0] > k &&
fruits[r][0] + startPos - fruits[l][0] * 2 > k) {
cur -= fruits[l][1];
l++;
}
res = max(res, cur);
}
return res;
}
};
2555. 两个线段获得的最多奖品 2081
题意:x轴上奖品,给一个整数数组,prizePositions, 按照非递减顺序排列,其中prizePositions[i]是滴i件奖品的位置。数轴上一个位置可能有多件奖品,再给一个整数k。 可以同时选择两个端点为整数的线段,每个线段长度必须为k。可以获得线段上的奖品。返回在选择两个最优线段前提下,获得的最多的奖品数量。
- 线段尽量不相交。
- 确定了右端点后左端点就确定了。
- prizePositions 的范围为1e9
- k 的范围是1e9
- prizePositions长度为1e5
- 两个线段相交肯定不是最优解,那么就等价于算两次最优线段吧。
/
code
2.2 越长越合法/求最短/最小
209. 长度最小的子数组
题意:给一个n个正整数的数组和一个正整数 target。找出满足总和大于等于target 的长度最小的
子数组 [nums_l, nums_l+1, ... , nums[r] ],并返回其长度。 如果不存在符合条件子数组,返回0 。
code
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n = nums.size();
int res = n + 1, sum = 0;
int l = 0, r = 0;
while (r < n) {
sum += nums[r];
while (sum - nums[l] >= target) {
sum -= nums[l++];
}
if (sum >= target) {
res = min(res, r - l + 1);
}
r++;
}
return res <= n ? res : 0;
}
};
3795. 不同元素和至少为 K 的最短子数组长度
题意:给一个整数数组nums和一个整数k
返回一个子数组的最小长度, 使得子数组中出现不同值之和至少为k。 如果不存在这样的子数组,返回 -1。
/
code
class Solution {
public:
int minLength(vector<int>& nums, int k) {
int n = nums.size();
int res = INT_MAX, sum = 0;
int l = 0, r = 0;
unordered_map<int, int> mp;
while (r < n) {
if (!mp[nums[r]]) {
sum += nums[r];
}
mp[nums[r]]++;
while (sum >= k) {
res = min(res, r - l + 1);
mp[nums[l]]--;
if (mp[nums[l]] == 0)
sum -= nums[l];
l++;
}
r++;
}
return res < INT_MAX ? res : -1;
}
};
2904. 最短且字典序最小的美丽子字符串
题意:给一个二进制字符串s 和一个整数k
如果s的某个子字符串中1的个数恰好等于k, 那么称这字符串是一个美丽字符串。
令 len 等于最短美丽子字符串的长度,返回长度等于len且字典序最小的美丽子字符串。 如果s 中不含美丽字符串,返回一个空串。
code
class Solution {
public:
string shortestBeautifulSubstring(string s, int k) {
int n = s.size();
string res = "";
if (ranges::count(s, '1') < k) {
return "";
}
res = s;
int l = 0, r = 0, cnt1 = 0;
while (r < n) {
if (s[r] == '1')
cnt1++;
while (cnt1 > k || s[l] == '0') {
cnt1 -= s[l++] - '0';
}
if (cnt1 == k) {
string t = s.substr(l, r - l + 1);
if (t.length() < res.length() ||
t.length() == res.length() && t < res) {
res = move(t);
}
}
r++;
}
return res;
}
};
1234. 替换子串得到平衡字符串
题意: 一个只包含 Q W E R 的 长度为n 的字符串。长度为n
假如在该字符串中,四个字符 都恰好出现n/4次,那么是一个平衡字符串。
给一个字符串s, 通过替换一个子串方式,使得原来的s 变成一个平衡的, 可以用长度相同的任何串来替换。
返回替换子串的最小可能长度。
如果本身就是平衡的,返回0.
code
class Solution {
public:
int balancedString(string s) {
int n = s.size();
int res = n;
unordered_map<char, int> mp;
for (int i = 0; i < n; i++)
mp[s[i]]++;
if (mp['Q'] == n / 4 && mp['W'] == n / 4 && mp['E'] == n / 4 &&
mp['R'] == n / 4)
return 0;
int l = 0, r = 0;
while (r < n) {
mp[s[r]]--;
while (mp['Q'] <= n / 4 && mp['W'] <= n / 4 && mp['E'] <= n / 4 &&
mp['R'] <= n / 4) {
res = min(res, r - l + 1);
mp[s[l]]++;
l++;
}
r++;
}
return res;
}
};
2875. 无限数组的最短子数组 1914
题意:给一个下标从 0 开始的数组 nums 和一个整数target
infinite_nums 是通过无限的将nums元素追加到自己之后生成的
从 infinite_nums 中找出满足元素和等于 target 的最短子数组,返回该数组长度,不存在返回-1
code
class Solution {
public:
int minSizeSubarray(vector<int>& nums, int target) {
int n = nums.size();
long long total = reduce(nums.begin(), nums.end(), 0LL);
int res = INT_MAX;
long long sum = 0;
int l = 0;
for (int r = 0; r < n * 2; r++) {
sum += nums[r % n];
while (sum > target % total) {
sum -= nums[l % n];
l++;
}
if (sum == target % total) {
res = min(res, r - l + 1);
}
}
return res == INT_MAX ? -1 : res + target / total * n;
}
};
76. 最小覆盖子串
题意: 给两个字符串 s 和 t, 长度分别是 m 和 n,返回s中的最短窗口的子串,使得该子串包含t 中的每一个字符(包括重复字符)
如果没有这样的子串,返回空字符串。
code
class Solution {
public:
string minWindow(string s, string t) {
int lens = s.size(), lent = t.size();
unordered_map<char, int> cnt;
int diff = 0, res = s.size() + 1;
for (int i = 0; i < lent; i++) {
if (cnt[t[i]] == 0)
diff++;
cnt[t[i]]++;
}
int l = 0;
int res_l = -1, res_r = lens;
for (int r = 0; r < lens; r++) {
cnt[s[r]]--;
if (cnt[s[r]] == 0)
diff--;
while (diff == 0) {
if (r - l < res_r - res_l) {
res_l = l;
res_r = r;
}
cnt[s[l]]++;
if (cnt[s[l]] == 1)
diff++;
l++;
}
}
return res_l < 0 ? "" : s.substr(res_l, res_r - res_l + 1);
}
};
632. 最小区间 (TODO)
题意:给一个 k 个非递减的整数列表, 找到一个最小区间 ,使得 k 个列表中的每个列表至少有一个数包含在其中。
// ps 这题先放放。没看懂。
2.3 求子数组个数
2.3.1 越短越合法
一般要写 res += r - l + 1
内层循环结束后 , [l,r] 这个子数组满足题目要求。由于子数组越短,越能满足要求,所以除了[l,r], 还有[l+1,r]... [r,r]。
713. 乘积小于 K 的子数组
题意:给一个整数数组nums 和一个整数k, 返回子数组中所有元素乘积 严格小于 k 的连续子数组数目;。
code
class Solution {
public:
int numSubarrayProductLessThanK(vector<int>& nums, int k) {
if (k <= 1)
return 0;
int n = nums.size();
long long mul = 1;
int res = 0;
int l = 0;
for (int r = 0; r < n; r++) {
mul *= nums[r];
while (mul >= k) {
mul /= nums[l];
l++;
}
res += r - l + 1;
}
return res;
}
};
3258. 统计满足 K 约束的子字符串数量 I 做到 O(n)
题意: 给一个二进制字符串s 和一个整数k
如果一个二进制字符串满足以下任意一个条件,那么认为该字符串满足k 约束: 1. 字符串中0的数量最多为k 2. 字符串中1的数量最多为k。
返回一个整数,表示s 中所有满足k 约束的子字符串数量。
code
class Solution {
public:
int countKConstraintSubstrings(string s, int k) {
int n = s.size();
int res = 0;
int l = 0, cnt0 = 0, cnt1 = 0;
for (int r = 0; r < n; r++) {
s[r] == '0' ? cnt0++ : cnt1++;
while (cnt0 > k && cnt1 > k) {
s[l] == '0' ? cnt0-- : cnt1--;
l++;
}
res += r - l + 1;
}
return res;
}
};
2302. 统计得分小于 K 的子数组数目 1808
题意:一个数组的分数定义为数组之和乘以数组的长度。
给一个正整数 nums 和一个整数 k。 返回nums中分数严格小于 k 的非空整数子数组数目。
code
class Solution {
public:
long long countSubarrays(vector<int>& nums, long long k) {
int n = nums.size();
long long res = 0;
long long sum = 0;
int l = 0;
for (int r = 0; r < n; r++) {
sum += nums[r];
while (1LL* sum * (r - l + 1) >= k) {
sum -= nums[l];
l++;
}
res += r - l + 1;
}
return res;
}
};
2762. 不间断子数组 1940
题意:给一个下标从 0 开始的整数数组nums, 子数组满足以下条件,那么他是不间断的。
i , i+1, ..., j表示下标。 对于满足 i<=i_1, i_2 <= j 的下标对,都有 0 <= |nums[i_1] - nums[i_2]| <=2 .
返回不间断子数组的总数目。
code
class Solution {
public:
long long continuousSubarrays(vector<int>& nums) {
long long res = 0;
int n = nums.size();
int l = 0;
map<int, int> cnt;
for (int r = 0; r < n; r++) {
cnt[nums[r]]++;
while (cnt.rbegin()->first - cnt.begin()->first > 2) {
cnt[nums[l]]--;
if (cnt[nums[l]] == 0)
cnt.erase(nums[l]);
l++;
}
res += r - l + 1;
}
return res;
}
};
LCP 68. 美观的花束
题意:一维数组 flowers 每个数字表示鲜花品种。可以选择一段区间鲜花做成插花,不能丢弃。在选择的区间,每一种花的数量不超过cnt,
那么认为是美观的。返回总共多少种可能的区间。
code
class Solution {
public:
int beautifulBouquet(vector<int>& flowers, int cnt2) {
int n = flowers.size();
int res = 0;
int l = 0;
unordered_map<int, int> cnt;
for (int r = 0; r < n; r++) {
cnt[flowers[r]]++;
while (cnt[flowers[r]] > cnt2) {
cnt[flowers[l]]--;
l++;
}
res += r - l + 1;
}
return res;
}
};
§2.3.2 越长越合法
一般会写 res += l
1358. 包含所有三种字符的子字符串数目 1646
题意:字符串s , 只包含三种字符a,b,c。 返回a,b,c 都至少出现过一次的子字符串的数目。
code
class Solution {
public:
int numberOfSubstrings(string s) {
int n = s.size();
int l = 0, res = 0;
int cnt[3] = {0, 0, 0};
for (int r = 0; r < n; r++) {
cnt[s[r] - 'a']++;
while (cnt[0] && cnt[1] && cnt[2]) {
cnt[s[l] - 'a']--;
l++;
}
res += l;
}
return res;
}
};
2962. 统计最大元素出现至少 K 次的子数组 1701
给一个整数数组nums 和一个正整数 k。
统计多少满足nums 中的最大元素至少出现k次的子数组, 并返回满足这一条件的子数组数目。
note : 注意题意 max 是整个nums数组的, 不是子数组的。
code
class Solution {
public:
long long countSubarrays(vector<int>& nums, int k) {
int n = nums.size();
int mx = ranges::max(nums);
map<int, int> cnt;
int l = 0, mxcnt = 0;
long long res = 0;
for (int r = 0; r < n; r++) {
mxcnt += nums[r] == mx ? 1 : 0;
while (mxcnt >= k) {
mxcnt -= mx == nums[l] ? 1 : 0;
l++;
}
res += l;
}
return res;
}
};
3325. 字符至少出现 K 次的子字符串 I 做到 O(n)
题意: 给一个字符串s 和一个整数k ,在s 的所有子字符串中,统计并返回至少有一个
字符至少出现k 次的子字符串总数。
code
class Solution {
public:
int numberOfSubstrings(string s, int k) {
int n = s.size();
int res = 0;
unordered_map<char, int> cnt;
int l = 0;
for (int r = 0; r < n; r++) {
cnt[s[r]]++;
while (cnt[s[r]] >= k) {
cnt[s[l]]--;
l++;
}
res += l;
}
return res;
}
};
2062. 统计字符串中的元音子字符串 做到 O(n)
题意:元音子字符串是仅由元音组成的子字符串,且必须包含全部5种元音
给一个字符串word, 统计并返回word 中元音子字符串的数目
注意: 遇到非元音 需要清空cnt。
code
class Solution {
public:
bool isvol(char c) {
return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
}
int countVowelSubstrings(string word) {
int n = word.size();
int lastv = 0;
int l = 0;
unordered_map<char, int> cnt;
int res = 0;
for (int r = 0; r < n; r++) {
if (isvol(word[r])) {
cnt[word[r]]++;
while (cnt['a'] && cnt['e'] && cnt['i'] && cnt['o'] &&
cnt['u']) {
cnt[word[l]]--;
l++;
}
res += l - lastv;
} else {
cnt.clear();
lastv = r + 1;
l = r + 1;
}
}
return res;
}
};
2799. 统计完全子数组的数目 做到 O(n)
题意: 一个正整数数组nums, 子数组满足以下条件,称为完全子数组:
子数组中不同元素数目,等于整个数组不同元素数目。
返回子数组中完全子数组数目。
ps: 熟练运用unordered_set, unordered_map 对代码简洁很有帮助。
code
class Solution {
public:
int countCompleteSubarrays(vector<int>& nums) {
int n = nums.size();
int res = 0, l = 0;
unordered_set<int> st(nums.begin(), nums.end());
unordered_map<int, int> cnt;
int diff = st.size();
for (int r = 0; r < n; r++) {
cnt[nums[r]]++;
while (cnt.size() == diff) {
cnt[nums[l]]--;
if (cnt[nums[l]] == 0) {
cnt.erase(nums[l]);
}
l++;
}
res += l;
}
return res;
}
};
2537. 统计好子数组的数目 1892
题意: 给一个整数数组nums 和一个k , 返回nums中好子数组的数目。
arr 至少 k 对下标 (i,j) 满足 i < j 且 arr[i] == arr[j] , 那么他是一个好子数组。
code
class Solution {
public:
long long countGood(vector<int>& nums, int k) {
long long res = 0;
int n = nums.size();
unordered_map<int, long long> cnt;
int l = 0;
long long pairs= 0 ;
for(int r = 0; r < n ;r++){
pairs += cnt[nums[r]];
cnt[nums[r]]++;
while(pairs >= k){
cnt[nums[l]]--;
pairs -= cnt[nums[l]];
l++;
}
res += l;
}
return res;
}
};
3298. 统计重新排列后包含另一个字符串的子字符串数目 II 1909 同 76 题
题意:给两个字符串 word1 和 word2
如果一个字符串 x 重新排列后, word2 是重排的前缀, 那么称 x 是合法的。
返回word1 中合法子字符串的数量。
code
class Solution {
public:
long long validSubstringCount(string word1, string word2) {
long long res = 0;
int len1 = word1.size(), len2 = word2.size();
int diff = 0;
unordered_map<int, int> cnt;
for (auto x : word2) {
if (!cnt[x])
diff++;
cnt[x]++;
}
int l = 0;
for (int r = 0; r < len1; r++) {
cnt[word1[r]]--;
if (cnt[word1[r]] == 0) {
diff--;
}
while (diff == 0) {
cnt[word1[l]]++;
if (cnt[word1[l]] == 1)
diff++;
l++;
}
res += l;
}
return res;
}
};
恰好型滑动窗口
930. 和相同的二元子数组 1592
题意: 给一个二元数组nums, 和一个整数 goal。统计并返回有多少个和为goal 的非空子数组。
和 >= goal 的 减去 和 > goal 的 就是恰好等于goal 的。
code
class Solution {
public:
int numSubarraysWithSum(vector<int>& nums, int goal) {
int n = nums.size();
int res = 0;
int l1 = 0, l2 = 0;
int sum1 = 0, sum2 = 0;
for (int r = 0; r < n; r++) {
sum1 += nums[r];
while (l1 <= r && sum1 >= goal) {
sum1 -= nums[l1];
l1++;
}
res += l1;
sum2 += nums[r];
while (sum2 > goal) {
sum2 -= nums[l2];
l2++;
}
res -= l2;
}
return res;
}
};
1248. 统计「优美子数组」 1624
题意:给一个整数数组nums,和一个整数k, 如果某个连续子数组中恰好有k 个奇数数字,我们认为是优美子数组。
返回数组中优美子数组个数。
ps: 这类题在>=时通常要判断l <=r ,因为窗口缩到0时可能还是满足,可能会越界,但是 > 条件一般不用。
code
class Solution {
public:
int numberOfSubarrays(vector<int>& nums, int k) {
int n = nums.size(), res = 0, l1 = 0, l2 = 0, sum1 = 0, sum2 = 0;
for (int r = 0; r < n; r++) {
sum1 += nums[r] % 2;
while (l1 <= r && sum1 >= k) {
sum1 -= nums[l1++] % 2;
}
res += l1;
sum2 += nums[r] % 2;
while (sum2 > k) {
sum2 -= nums[l2++] % 2;
}
res -= l2;
}
return res;
}
};
3306. 元音辅音字符串计数 II 2200
题意:给一个字符串word 和一个非负整数 k。
返回word 的子字符串中, 每个元音字母至少出现一次,且恰好包含k个辅音字符的子字符串总数。
code
class Solution {
public:
bool isVol(char c) {
return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
}
long long f(string& word, int k) {
long long res = 0;
unordered_map<char, int> cnt;
int n = word.size(), l = 0;
int sum = 0;
for (int r = 0; r < n; r++) {
bool isvol = isVol(word[r]);
if (isvol)
cnt[word[r]]++;
else
sum++;
while (cnt.size() == 5 && sum >= k) {
char out = word[l];
if (isVol(out)) {
if (--cnt[out] == 0) {
cnt.erase(out);
}
} else {
sum--;
}
l++;
}
res += l;
}
return res;
}
long long countOfSubstrings(string word, int k) {
return f(word, k) - f(word, k + 1);
}
};
992. K 个不同整数的子数组 2210
题意:给一个正整数数组 nums 和一个整数k 。返回nums 中好子数组的数目
如果nums 的某个子数组中不同整数 的个数恰好为k ,则称nums这个连续、不一定不同的子数组为好子数组。
code
class Solution {
public:
int f(vector<int>& nums, int k) {
int n = nums.size(), res = 0, l = 0;
unordered_map<int, int> cnt;
int diff = 0;
for (int r = 0; r < n; r++) {
cnt[nums[r]]++;
if (cnt[nums[r]] == 1)
diff++;
while (diff >= k) {
cnt[nums[l]]--;
if (cnt[nums[l]] == 0)
diff--;
l++;
}
res += l;
}
return res;
}
int subarraysWithKDistinct(vector<int>& nums, int k) {
return f(nums, k) - f(nums, k + 1);
}
};
3859. 统计包含 K 个不同整数的子数组 2302
题意:给一个整数数组nums 和两个整数 k 和 m
返回一个整数,表示满足以下条件的子数组的数量。
- 子数组恰好包含 k 个不同整数
- 在子数组中, 每个不同的整数至少出现 m 次
code
class Solution {
public:
long long f(vector<int>& nums, int k, int limit, int m) {
long long res = 0;
unordered_map<int, int> cnt;
int n = nums.size(), l = 0, ge_m = 0;
for (int r = 0; r < n; r++) {
cnt[nums[r]]++;
if (cnt[nums[r]] == m)
ge_m++;
while (cnt.size() >= k && ge_m >= limit) {
int out = nums[l];
if (cnt[out] == m) {
ge_m--;
}
if (--cnt[out] == 0) {
cnt.erase(out);
}
l++;
}
res += l;
}
return res;
}
long long countSubarrays(vector<int>& nums, int k, int m) {
return f(nums, k, k, m) - f(nums, k + 1, k, m);
}
};
浙公网安备 33010602011771号