标题:[leetcode]滑动窗口(二)
@水墨不写bug
目录
正文开始:
(一)水果成篮
你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组
fruits
表示,其中fruits[i]
是第i
棵树上的水果 种类 。你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:
- 你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
- 你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
- 一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。
给你一个整数数组
fruits
,返回你可以收集的水果的 最大 数目。示例 1:
输入:fruits = [1,2,1] 输出:3 解释:可以采摘全部 3 棵树。示例 2:
输入:fruits = [0,1,2,2] 输出:3 解释:可以采摘 [1,2,2] 这三棵树。 如果从第一棵树开始采摘,则只能采摘 [0,1] 这两棵树。示例 3:
输入:fruits = [1,2,3,2,2] 输出:4 解释:可以采摘 [2,3,2,2] 这四棵树。 如果从第一棵树开始采摘,则只能采摘 [1,2] 这两棵树。示例 4:
输入:fruits = [3,3,3,1,2,1,1,2,3,3,4] 输出:5 解释:可以采摘 [1,2,1,1,2] 这五棵树。提示:
1 <= fruits.length <= 105
0 <= fruits[i] < fruits.length
class Solution {
public:
int totalFruit(vector<int>& f) {
//unordered_map<int,int> hash;
int hash[100001] = {0};
int n = f.size(),ret = 0,kinds = 0;
for(int left = 0,right = 0 ; right < n; ++right)
{
//进窗口
if(hash[f[right]] == 0)
++kinds;
hash[f[right]]++;
if(kinds > 2)//判断
{
hash[f[left]]--;//出窗口
if(hash[f[left]] == 0)
{
--kinds;
}
left++;
}
//更新结果
ret = max(ret,right-left+1);
}
return ret;
}
};
(二)找到字符串中所有字母异位词
给定两个字符串
s
和p
,找到s
中所有p
的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
示例 1:
输入: s = "cbaebabacd", p = "abc" 输出: [0,6] 解释: 起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。 起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。示例 2:
输入: s = "abab", p = "ab" 输出: [0,1,2] 解释: 起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。 起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。 起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。提示:
1 <= s.length, p.length <= 3 * 104
s
和p
仅包含小写字母
class Solution {
public:
inline bool is_move(const int hash1[26],const int hash2[26])
{
for(int i = 0;i <= 25;i++)//错写成26导致一直栈区越界访问
{
if(hash1[i] != hash2[i])
{
return false;
}
}
return true;
}
vector<int> findAnagrams(string s, string p) {
vector<int> v;
int hash1[26] = {0},hash2[26] = {0},ns = s.size(),ps = p.size();
for(const auto& e:p) hash2[e-'a']++;//统计p中每一个字符出现个数
for(int left = 0,right = 0 ; right < ns ; ++right)
{
//进窗口
hash1[s[right]-'a']++;
if(right-left+1 > ps)//判断
{
hash1[s[left++]-'a']--;//出窗口
}
if(right-left+1 == ps)
{
if(is_move(hash1,hash2))
{
v.push_back(left);//更新结果
}
}
}
return v;
}
};
(三)串联所有单词的子串
给定一个字符串
s
和一个字符串数组words
。words
中所有字符串 长度相同。
s
中的 串联子串 是指一个包含words
中所有字符串以任意顺序排列连接起来的子串。
- 例如,如果
words = ["ab","cd","ef"]
, 那么"abcdef"
,"abefcd"
,"cdabef"
,"cdefab"
,"efabcd"
, 和"efcdab"
都是串联子串。"acdbef"
不是串联子串,因为他不是任何words
排列的连接。返回所有串联子串在
s
中的开始索引。你可以以 任意顺序 返回答案。示例 1:
输入:s = "barfoothefoobarman", words = ["foo","bar"] 输出:[0,9]
解释:因为 words.length == 2 同时 words[i].length == 3,连接的子字符串的长度必须为 6。 子串 "barfoo" 开始位置是 0。它是 words 中以 ["bar","foo"] 顺序排列的连接。 子串 "foobar" 开始位置是 9。它是 words 中以 ["foo","bar"] 顺序排列的连接。 输出顺序无关紧要。返回 [9,0] 也是可以的。示例 2:
输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]输出:[]
解释:因为 words.length == 4 并且 words[i].length == 4,所以串联子串的长度必须为 16。 s 中没有子串长度为 16 并且等于 words 的任何顺序排列的连接。 所以我们返回一个空数组。示例 3:
输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"] 输出:[6,9,12] 解释:因为 words.length == 3 并且 words[i].length == 3,所以串联子串的长度必须为 9。 子串 "foobarthe" 开始位置是 6。它是 words 中以 ["foo","bar","the"] 顺序排列的连接。 子串 "barthefoo" 开始位置是 9。它是 words 中以 ["bar","the","foo"] 顺序排列的连接。 子串 "thefoobar" 开始位置是 12。它是 words 中以 ["the","foo","bar"] 顺序排列的连接。提示:
1 <= s.length <= 104
1 <= words.length <= 5000
1 <= words[i].length <= 30
words[i]
和s
由小写英文字母组成
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector<int> ret;
int len = words[0].size(),n = words.size();
unordered_map<string,int> hash2;//words中元素的出现次数
for(const auto& str : words) hash2[str]++;
for(int i = 0;i < len;++i)//执行len次
{
unordered_map<string,int> hash1;//s中元素出现的次数
for( int left = i,right = i,count = 0 ; right + len <= s.size()/*细节<=*/ ; right += len)
{
//进窗口
string in = s.substr(right,len);
hash1[in]++;
if(hash2.count(in) && hash1[in] <= hash2[in]) ++count;
//判断
if(right-left+1 > n*len)
{
string out = s.substr(left,len);
if(hash2.count(out) && hash1[out] <= hash2[out]) --count;
hash1[out]--;//出窗口
left += len;
}
//更新结果
if(count == n) ret.push_back(left);
}
}
return ret;
}
};
(四)覆盖最小子串
给你一个字符串
s
、一个字符串t
。返回s
中涵盖t
所有字符的最小子串。如果s
中不存在涵盖t
所有字符的子串,则返回空字符串""
。注意:
- 对于
t
中重复字符,我们寻找的子字符串中该字符数量必须不少于t
中该字符数量。- 如果
s
中存在这样的子串,我们保证它是唯一的答案。示例 1:
输入:s = "ADOBECODEBANC", t = "ABC" 输出:"BANC" 解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。示例 2:
输入:s = "a", t = "a" 输出:"a" 解释:整个字符串 s 是最小覆盖子串。示例 3:
输入: s = "a", t = "aa" 输出: "" 解释: t 中两个字符 'a' 均应包含在 s 的子串中, 因此没有符合条件的子字符串,返回空字符串。提示:
m == s.length
n == t.length
1 <= m, n <= 105
s
和t
由英文字母组成进阶:你能设计一个在
o(m+n)
时间内解决此问题的算法吗?
class Solution {
public:
string minWindow(string s, string t) {
int hash1[128] = {0};//存储t有效字母个数
int kinds = 0;
for(const auto& e : t)
{
if(hash1[e] == 0) kinds++;
hash1[e]++;
}
int hash2[128] = {0};//存储窗口内有效字母个数
int n = s.size(),min_len = INT_MAX,begin = -1;
for(int left = 0,right = 0,count = 0;right < n;right++)
{
//进窗口
char in = s[right];
hash2[in]++;
if(hash2[in] == hash1[in]) count++;
//判断
while(count == kinds)
{
//更新结果
int len = right-left+1;
if(len < min_len)
{
min_len = len;
begin = left;
}
//出窗口
char out = s[left++];
if(hash1[out] == hash2[out]) count--;
hash2[out]--;
}
}
if(begin == -1) return "";
return s.substr(begin,min_len);
}
};
完~
未经作者同意禁止转载