滑动窗口相关题目
LeetCode 3. 无重复字符的最长子串
题目描述:
给定一个字符串,找出不含有重复字符的最长子串的长度。
示例说明:
给定 "abcabcbb" ,没有重复字符的最长子串是 "abc" ,那么长度就是3。
给定 "bbbbb" ,最长的子串就是 "b" ,长度是1。
给定 "pwwkew" ,最长子串是 "wke" ,长度是3。请注意答案必须是一个子串,"pwke" 是 子序列 而不是子串。
题目分析:
比较容易想到用滑动窗口来解决该问题,因为该问题是一个子串,是一个连续的字符组成,符合窗口问题的描述--连续的某一个部分(在整体中),根据具体问题设计相应的窗口的滑动、调整情况。该题中是找不含重复字母的最长子串的程度,需要判断什么时候出现重复的字母,一旦出现,把前一个时刻的子串长度进行保留即可。(比较去最大值)
具体代码:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int freq[256]={0};
int l=0,r=-1; //注意初始化以及后边的判断条件
int res=0;
while(l<s.size())
{
if(r+1<s.size()&&freq[s[r+1]]==0)
freq[s[++r]]++;
else
{
res=max(res,r-l+1); //位置应该出现在出现重复之后(上边的if条件不满足)
freq[s[l++]]--;
}
}
return res;
}
};
LeetCode 438. 找到字符串中所有字母异位词
题目描述:
给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。
字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。
说明:
字母异位词指字母相同,但排列不同的字符串。
不考虑答案输出的顺序
示例说明:
示例 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" 的字母异位词。
题目分析:
思想同上,主要是感受一下,窗口的感觉。可以结合代码看。
具体代码:
//第一种实现方式,比较直接,易懂
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
//用滑动窗口来做
vector<int> ss(256,0),pp(256,0),res;
for(int i=0;i<p.size();i++) //注意位数
{
pp[p[i]]++; //今后不做改变
ss[s[i]]++;
}
if(pp==ss) //两个字符串相等
res.push_back(0);
for(int i = p.size(); i < s.size(); ++i)
{
ss[s[i]]++; //新的起点进行查找
ss[s[i-p.size()]]--; //前边的进行恢复(--操作)
if(pp == ss)
res.push_back(i-p.size()+1);
}
return res;
}
};
//第二种实现方式,不容易理解透,比较偏
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
vector<int> res;
if(p.size()>s.size()||p.size()==0) return res;
int freq[256]={0};
for(int i = 0;i<p.size();++i)
freq[p[i]]++;
int l = 0, r = 0;
int len = p.size();
while(r<s.size())
{
if(freq[s[r++]]-->=1)
len--;
if(len == 0) res.push_back(l);
if(r-l== p.size() && freq[s[l++]]++>=0)
len++;
}
return res;
}
};
剑指offer 65. 滑动窗口的最大值
题目描述:
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值
示例说明:
例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,
他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个:
{[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1},
{2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
具体代码:(代码上附有详细的解析,这里就不展开讲述了)
class Solution {
public:
//滑动窗口其实就是一个局部区域找最大值,步长为1的从头开始向后运动。
//最好能提前都确定好输出的尺寸,几卷积之后的尺寸。如果num长x,size=s;则结果尺寸为x-s+1
//滑动窗口应该是队列,但是什么样的队列能解决---单调的
vector<int> maxInWindows(const vector<int>& num, unsigned int size)
{
vector<int> vec;
if(num.size()<=0 || num.size()<size ||size<=0) return vec;//处理特殊情况
deque<int> dq;
//处理前size个数据,因为这个时候不需要输出最大值;
for(unsigned int i=0;i<size;i++)
{
//假如当前的元素比队列队尾的元素大,说明之前加入的这些元素不可能是最大值了。因为当前的这个数字比之前加入队列的更晚
while(!dq.empty()&&num[i]>=num[dq.back()])
dq.pop_back();//弹出比当前小的元素下标
dq.push_back(i);//队尾压入当前下标
}
//处理size往后的元素,这时候需要输出滑动窗口的最大值
for(unsigned int i=size;i<num.size();i++)
{
vec.push_back(num[dq.front()]);
while(!dq.empty()&&num[i]>=num[dq.back()])
dq.pop_back();
if(!dq.empty() && dq.front()<=(int)(i-size))//判断队头的下标是否超出size(窗口)大小,如果超过,要删除队头元素
dq.pop_front();//删除队头元素
dq.push_back(i);//将当前下标压入队尾,因为可能在未来是最大值
}
vec.push_back(num[dq.front()]);//最后还要压入一次
return vec;
}
};
LeetCode 76. 最小覆盖子串
题目描述:
给定一个字符串 S 和一个字符串 T,请在 S 中找出包含 T 所有字母的最小子串。
示例说明:
输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"
说明:
如果 S 中不存这样的子串,则返回空字符串 ""。
如果 S 中存在这样的子串,我们保证它是唯一的答案。
注:这道题目可以感受一下,属于比较难的题目。