滑动窗口问题复习
Posted 小智RE0
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了滑动窗口问题复习相关的知识,希望对你有一定的参考价值。
文章目录
当然可以在B站找一些教学视频学习
简单来说;滑动窗口的思想就是
(1)当不满足当前的条件时,向右扩充,当满足条件时,向右收缩左边界,得到一个解后暂时保存,
(2)循环第一步,又得到一个解,将其与第一个解相对比,得到最优解并暂存,以此类推,直到窗口到达右边界停止。
1.基础题入手
1.1 力扣[3]: 无重复字符的最长子串
原题链接:无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
示例 4:
输入: s = ""
输出: 0
提示:
0 <= s.length <= 5 * 104
s 由英文字母、数字、符号和空格组成
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
使用滑动窗口解决
class Solution
public int lengthOfLongestSubstring(String s)
//滑动窗口思想;
//在字符串中逐步向右移动;若有重复则左边界收缩;
int n = s.length();
if(n<2)
return n;
//这里可用字符数组来标记char字符;
//使用int[] 数组来统计字符的频率数;
char[] cArr = s.toCharArray();
//s 由英文字母、数字、符号和空格组成;
int[] nums = new int[128];
//窗口的两个指针;
int left = 0;
int right = 0;
//结果至少是1;
int res = 1;
//只要窗口不超出字符串有边界范围;即可操作;
while(right<n)
//记录当前字符的频率数;
nums[cArr[right]]++;
//注意滑动时若重复则收缩左指针边界;
while(nums[cArr[right]]==2)
nums[cArr[left]]--;
left++;
//算长度;
res = Math.max(res,right-left+1);
right++;
return res;
也可用set集合的不重复性,配合指针解决
class Solution
public int lengthOfLongestSubstring(String s)
//字符串匹配过程;
if(s==null || s.length()==0)
return 0;
//指针解法;
Set<Character> set = new HashSet<>();
int zhen = -1; int res =0;
for(int i =0;i<s.length();++i)
if(i!=0)
set.remove(s.charAt(i-1));
//不重复就移动;
while(zhen+1<s.length() && !set.contains(s.charAt(zhen+1)))
set.add(s.charAt(zhen+1));
++zhen;
//该段不重复字符的长度;
res = Math.max(res,zhen-i+1);
return res;
1.2 力扣[209]: 长度最小的子数组
原题位置:长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的
连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。
如果不存在符合条件的子数组,返回 0 。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
示例 2:
输入:target = 4, nums = [1,4,4]
输出:1
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
提示:
1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-size-subarray-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
滑动窗口
class Solution
public int minSubArrayLen(int target, int[] nums)
//滑动窗口思想;
int left = 0;
int right = 0;
int n = nums.length;
int sum = 0;
//设定一个默认的长度;
int len = Integer.MAX_VALUE;
while(right < n)
//先移动右指针,去加数据;
sum+=nums[right];
//若>=目标值;由于要满足最短的子数组,则收缩左边界;
while(sum>=target)
len = Math.min(len,right - left +1);
//收缩左边界;
sum -= nums[left];
left++;
right ++;
//由于len初始化为最大值了;这里需要排除;
if(len == Integer.MAX_VALUE)
return 0;
return len;
2.其他题
基本还是用了滑动窗口的思想;需要考虑额外的要求;
2.1 力扣[424] :替换后的最长重复字符
原题链接:替换后的最长重复字符
给定一个字符串 s 和一个整数 k 。
您可以选择字符串中的任意字符,并将其更改为任何其他大写英文字符。该操作最多可执行 k 次。
在执行上述操作后,返回 包含相同字母的最长子字符串的长度 。
示例 1:
输入:s = "ABAB", k = 2
输出:4
解释:用两个'A'替换为两个'B',反之亦然。
示例 2:
输入:s = "AABABBA", k = 1
输出:4
解释:
将中间的一个'A'替换为'B',字符串变为 "AABBBBA"。
子串 "BBBB" 有最长重复字母, 答案为 4。
提示:
1 <= s.length <= 105
s 由小写英文字母组成
0 <= k <= s.length
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/longest-repeating-character-replacement
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
使用滑动窗口,逐渐向右匹配,同时记录当前窗口内的重复字符个数;若在替换k次
后,逼近于窗口的长度;那么当前窗口的长度即符合条件,返回即可;
class Solution
public int characterReplacement(String s, int k)
int len = s.length();
if(len < 2) return len;
//字符串转为数组;
char[] Arr = s.toCharArray();
//记录字符频率的数组;
int[] temp = new int[128];
//当窗口滑动时,此时的窗口内当前的字符个数;
int tempCount =0;
//最终结果;
int res = 0;
//滑动指针;
int left = 0;
int right = 0;
while(right < len)
temp[Arr[right]]++;
//记录个数;
tempCount = Math.max(tempCount,temp[Arr[right]]);
right ++;
//如果当前的字符无法将窗口填充,需要将左窗口收缩;
if(tempCount+k < right - left)
temp[Arr[left]]--;
left++;
//记录此时的长度;
res = Math.max(res,right - left);
return res;
2.2 力扣[76] : 最小覆盖子串
原题位置:最小覆盖子串
- 给你一个字符串 s 、一个字符串 t 。
- 返回 s 中涵盖 t 所有字符的最小子串。
- 如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。
注意:
对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
如果 s 中存在这样的子串,我们保证它是唯一的答案。
示例 1:
输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"
示例 2:
输入:s = "a", t = "a"
输出:"a"
示例 3:
输入: s = "a", t = "aa"
输出: ""
解释: t 中两个字符 'a' 均应包含在 s 的子串中,
因此没有符合条件的子字符串,返回空字符串。
提示:
1 <= s.length, t.length <= 105
s 和 t 由英文字母组成
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-window-substring
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
父串匹配子串问题,利用窗口找到匹配的字符起点位置;
如何保证匹配的字符相等?
-使用字符统计数组来统计当前字符的出现次数,若一致则说明匹配;
class Solution
public String minWindow(String s, String t)
//....当时 没注意,把faLen 和sonLen的长度都用了s的,拍错好久;
//s:父串, t:子串;
int faLen = s.length();
int sonLen = t.length();
//特殊情况排除;
if(faLen < sonLen)
return "";
//这里先将两个字符串变为字符数组;
char[] FaArr = s.toCharArray();
char[] SonArr = t.toCharArray();
//用于统计字符频率数的数组;
int[] FaCount = new int[128];
int[] SonCount = new int[128];
//子串固定的了,那就直接赋予;
for(char c:SonArr)
SonCount[c]++;
//记录当前的划到的窗口内 包含的子串字符种类个数;
int FaDistance = 0;
//滑动窗口;
int left = 0;
int right = 0;
//父串中满足条件的串长度;
int goodLen = Integer.MAX_VALUE;
//父串中符合条件的索引起点;
int begin = 0;
while(right < faLen)
//当前的字符出现数量一样了;那么就增加父串的种类数;
if(FaCount[FaArr[right]] < SonCount[FaArr[right]])
FaDistance ++;
//记录当前字符出现的次数;
FaCount[FaArr[right]]++;
//父串先向右移动;
right++;
//注意;若果在父串中已经完全找到了子串;可考虑开始优化;
while(FaDistance == sonLen)
//这里算一下长度,同时记录符合的左窗口起点;
if(goodLen > right-left)
goodLen = right-left;
begin = left;
//若左指针对应的字符量此时已经匹配子串的量;则字符串的种类数减少;
if(FaCount[FaArr[left]] == SonCount[FaArr[left]])
FaDistance --;
FaCount[FaArr[left]]--;
//左窗口缩进;
left++;
//这里要对满足的子串长度判断;
if(goodLen ==Integer.MAX_VALUE )
return "";
//截取符合的字符串;
return s.substring(begin,begin+goodLen);
2.3 力扣[438] :找到字符串中所有字母异位词
原题位置: 找到字符串中所有字母异位词
给定两个字符串 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 仅包含小写字母
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-all-anagrams-in-a-string
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这道题,在76题的基础上,稍微改动之处,
需要完全匹配子串的 长度和 字符;
class Solution
public List<Integer> findAnagrams(String s, String p)
List<Integer> list = new ArrayList<>();
//滑动窗口问题;
int FaLen = s.length();
int SonLen = p.length();
if(FaLen<SonLen)
return list;
//先将子字符串转为数组;
char[] SonArr = p.toCharArray();
//统计字符串频率数的数组;
int[] FaCount = new int[128];
int[] SonCount= new int[128];
//将子串的字符出现次数统计;
for(char c : SonArr)
SonCount[c]++;
//父串中符合的字符种类数;
int FaDistance =0;
//子串当前的字符种类数;
int SonDistance = 0;
for(int i =0;i<SonCount.length;i++)
if(SonCount[i]>0)
SonDistance+=1;
int left =0;
int right =0;
while(right < FaLen)
//窗口先向右滑动;
char rightChar = s.charAt(right);
//若当前子串有此字符再行动;
if(SonCount[rightChar]>0)
FaCount[rightChar]++;
if(FaCount[rightChar] == SonCount[rightChar])
FaDistance++;
right++;
//当符合子串长度时;
while(FaDistance == SonDistance)
//标记合适的起点;
if(SonLen == right-left)
list.add(left);
char leftChar = s.charAt(left);
//左窗口收缩;
if(SonCount[leftChar]>0)
FaCount[leftChar]--;
if(FaCount[leftChar]<最长的最美好子字符串 java版本 滑动窗口解法并配有注释