
长度最小的子数组
题目要求我们求子数组和>=目标值的最小长度,即需要在满足条件的情况下统计最小长度。
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n = nums.size(), ret = INT_MAX, sum = 0;
for (int l = 0, r = 0; r < nums.size(); r++)
{
sum += nums[r];
while (sum >= target)
{
ret = min(ret, r - l + 1);
sum -= nums[l++];
}
}
return ret == INT_MAX ? 0 : ret;
}
};
无重复字符的最长子串
用数组模拟哈希表,统计进入窗口的字符的个数,以便统计最长子串的长度。
注意统计长度的位置,应该在不含有重复字符后统计结果,和前面一题区分。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int hash[200] = {};
int n = s.size(), len = 0;
for (int l = 0, r = 0; r < n; r++)
{
hash[s[r]]++; // 进窗口
while (hash[s[r]] > 1)
{
hash[s[l++]]--; // 出窗口
}
len = max(len, r - l + 1);
}
return len;
}
};
dd爱框框
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n, x;
cin >> n >> x;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++)
cin >> a[i];
int left = -1, right = -1, sum = 0, len = INT_MAX;
for (int l = 1, r = 1; r <= n; r++)
{
sum += a[r]; // 1.进窗口
while (sum >= x) // 2.判断
{
if (r - l + 1 < len)
{
// 3.更新结果
left = l, right = r;
len = right - left + 1;
}
sum -= a[l++]; // 4.出窗口
}
}
cout << left << " " << right << endl;
return 0;
}
最大连续1的个数 III
反着来思考,翻转k个0让数组连续1最长,也就是允许一段连续1的子数组里有k个0,可以维护一段窗口,其中的0的个数不超过k就行。
class Solution {
public:
int longestOnes(vector<int>& nums, int k) {
int n = nums.size(), ret = 0, zero = 0;
for (int l = 0, r = 0; r < n; r++)
{
if (nums[r] == 0) zero++; // 进窗口
while (zero > k) // 当条件不满足时
{
if (nums[l++] == 0) zero--; // 出窗口
}
ret = max(ret, r - l + 1); // 统计结果
}
return ret;
}
};
将 x 减到 0 的最小操作数
减去数组左边或右边的元素到刚好为给定的x,正着不好求可以考虑反着来,求数组中连续的最长子数组,其和为整个数组之和-x。
class Solution {
public:
int minOperations(vector<int>& nums, int x) {
int n = nums.size(), sum = 0, len = -1, tmp = 0;
for (auto e : nums) sum += e;
int target = sum - x;
if (target < 0) return -1;
for (int l = 0, r = 0; r < n; r++)
{
tmp += nums[r];
while (tmp > target)
{
tmp -= nums[l++];
}
if (tmp == target)
len = max(len, r - l + 1);
}
return len == -1 ? len : n - len;
}
};
水果成篮
用数组模拟哈希表统计水果种类个数,如果哈希表中水果种类对应位置为0则种类++,出窗口哈希表中对应位置值–,当减小到0时种类减一。
class Solution {
public:
int totalFruit(vector<int>& fruits) {
int hash[100001] = {};
int n = fruits.size(), len = 0, kinds = 0;
for (int l = 0, r = 0; r < n; r++)
{
if (hash[fruits[r]] == 0) kinds++;
hash[fruits[r]]++; // 进窗口
while (kinds > 2) // 条件判断
{
if (--hash[fruits[l++]] == 0) kinds--; // 出窗口,跟新条件
}
len = max(len, r - l + 1); // 更新结果
}
return len;
}
};
找到字符串中所有字母异位词
用一个count变量统计窗口中对应p中字符的个数,只有当窗口大小等于p的长度,并且对应字符个数相同才符合要求。
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int hash1[26] = {};
int n = p.size();
for (auto ch : p) hash1[ch - 'a']++; // 统计p中各字符出现的次数
int hash2[26] = {};
vector<int> ret;
for (int l = 0, r = 0, count = 0; r < s.size(); r++)
{
// 进窗口
if (++hash2[s[r] - 'a'] <= hash1[s[r] - 'a']) count++;
while (r - l + 1 > n)
{
int t = s[l++] - 'a'; // 出窗口
if (hash2[t]-- <= hash1[t]) count--;
}
if (count == n) ret.push_back(l); // 窗口内对应字符个数等于p的长度才符合要求
}
return ret;
}
};
串联所有单词的子串
本质还是和上一题字母异位词一样,从目标字符串中找指定个子串任意排列的连接的子串,只不过本题需要枚举子串长度遍,在遍历的过程中注意范围,和一步需要走多长的距离。
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
unordered_map<string, int> hash1;
for (auto& e : words) hash1[e]++;
int len = words[0].size(), n = words.size();
vector<int> ret;
for (int i = 0; i < len; i++) // 以[0, len)中的位置为起点
{
unordered_map<string, int> hash2; // hash2每次都要更新
// 注意边界处理,保证能构成一个长度为len的子串
for (int l = i, r = i, count = 0; r + len <= s.size(); r += len)
{
string in = s.substr(r, len);
if (++hash2[in] <= hash1[in]) count++;
while (r - l + 1 > n * len)
{
string out = s.substr(l, len);
l += len; // 出窗口
if (hash2[out]-- <= hash1[out]) count--;
}
if (count == n) ret.push_back(l);
}
}
return ret;
}
};
最小覆盖子串
本题和上面的两题有点区别,上面两题是字符或子串拼接而成,其中的字符和子串对应的个数必须是相同的,但本题要找的是覆盖所有字符的最小子串,字符对应的个数可以不相等,但必须保证每个字符都存在。
class Solution {
public:
string minWindow(string s, string t) {
int hash1[128] = {};
int kinds = 0; // t中字符种类个数
for (auto e : t)
if (hash1[e]++ == 0)
kinds++;
int hash2[128] = {};
int begin = -1, len = INT_MAX;
for (int l = 0, r = 0, count = 0; r < s.size(); r++)
{
// 进窗口
char in = s[r];
// 当窗口中in字符的个数等于t中in字符的个数时
if (++hash2[in] == hash1[in]) count++;
while (count == kinds)
{
if (r - l + 1 < len)
{
len = r - l + 1;
begin = l;
}
// 出窗口
char out = s[l++];
if (hash2[out]-- == hash1[out]) count--;
}
}
return begin == -1 ? "" : s.substr(begin, len);
}
};
class Solution {
public:
string minWindow(string s, string t) {
unordered_map<char, int> hash1;
auto hash2 = hash1;
for (auto e : t) hash1[e]++;
int begin = 0, len = INT_MAX;
for (int l = 0, r = 0, count = 0; r < s.size(); r++)
{
if (++hash2[s[r]] <= hash1[s[r]]) count++;
while (count == t.size())
{
if (r - l + 1 < len)
{
len = r - l + 1;
begin = l;
}
if (hash2[s[l]]-- <= hash1[s[l++]]) count--;
}
}
return len == INT_MAX ? "" : s.substr(begin, len);
}
};
DNA序列
固定长度的滑动窗口问题,当满足条件时更新结果。
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str;
int n;
cin >> str >> n;
int len = str.size();
if (len <= n)
{
cout << str << endl;
return 0;
}
int cnt = 0;
for (int i = 0; i < n; i++)
if (str[i] == 'G' || str[i] == 'C')
cnt++;
int begin = 0, tmp = cnt;
for (int l = 0, r = n - 1; r + 1 < len;)
{
char a = str[l++], b = str[++r];
if (a == 'G' || a == 'C') tmp--;
if (b == 'G' || b == 'C') tmp++;
if (tmp > cnt)
{
begin = l;
cnt = tmp;
}
}
cout << str.substr(begin, n) << endl;
return 0;
}
本篇文章的分享就到这里了,如果您觉得在本文有所收获,还请留下您的三连支持哦~
