文章目录
- 滑动窗口简介
- 最小覆盖字串问题
- 长度最小的数组
- 找到字符串中所有字母异位词
- 水果成篮
- 无重复的最长子串3
- 未完待续
滑动窗口简介
滑动窗口算法是在给定特定窗口大小的数组或字符串上执行要求的操作。
This technique shows how a nested for loop in few problems can be converted to single for loop and hence reducing the time complexity.
该技术可以将一部分问题中的嵌套循环转变为一个单循环,因此它可以减少时间复杂度。其实这里就可以看出来滑动窗口主要应用在数组和字符串上。
可以用来解决一些查找满足一定条件的连续区间的性质(长度等)的问题。由于区间连续,因此当区间发生变化时,可以通过旧有的计算结果对搜索空间进行剪枝,这样便减少了重复计算,降低了时间复杂度。往往类似于“ 请找到满足 xx 的最 x 的区间(子串、子数组)的 xx ”这类问题都可以使用该方法进行解决。
在介绍滑动窗口的框架时候,大家先从字面理解下:
- 滑动:说明这个窗口是移动的,也就是移动是按照一定方向来的。
- 窗口:窗口大小并不是固定的,可以不断扩容直到满足一定的条件;也可以不断缩小,直到找到一个满足条件的最小窗口;当然也可以是固定大小。
为了便于理解,这里采用的是字符串来讲解。但是对于数组其实也是一样的。滑动窗口算法的思路是这样:
- 我们在字符串 S 中使用双指针中的左右指针技巧,初始化 left = right = 0,把索引闭区间 [left, right]称为一个「窗口」。
- 我们先不断地增加 right 指针扩大窗口 [left, right],直到窗口中的字符串符合要求(包含了 T 中的所有字符)。
- 此时,我们停止增加 right,转而不断增加 left 指针缩小窗口 [left, right],直到窗口中的字符串不再符合要求(不包含 T 中的所有字符了)。同时,每次增加 left,我们都要更新一轮结果。
重复第 2 和第 3 步,直到 right 到达字符串 S 的尽头。
最小覆盖字串问题
对于最小覆盖子串问题:
伪代码
string s, t;
// 在 s 中寻找 t 的「最小覆盖子串」
int left = 0, right = 0;
string res = s;
while(right < s.size()) {
window.add(s[right]);
right++;
// 如果符合要求,移动 left 缩小窗口
while (window 符合要求) {
// 如果这个窗口的子串更短,则更新 res
res = minLen(res, window);
window.remove(s[left]);
left++;
}
}
return res;
滑动窗口问题抽象
int left = 0, right = 0;
while (right < s.size()) {
window.add(s[right]);
right++;
while (valid) {
window.remove(s[left]);
left++;
}
}
实际代码
public String minWindow(String s, String t) {
if(s.length()==0||t.length()==0|| t.length()>s.length())
return "";
int left=0;
int right=0,flag=0;
String result;
HashMap<Character, Integer> needed = new HashMap<>();
// t.charAt是自己百度得到的
// 不可能出现重复字符
//设置满足条件为needed hash 所有vlue 为负则满足匹配条件
for(int i=0;i<t.length();i++){
needed.put(t.charAt(i),needed.getOrDefault(t.charAt(i),0)+1);
}
String res=s;
while (right<s.length()){
if(needed.containsKey(s.charAt(right))){
//包含在need中need-1
needed.put(s.charAt(right),needed.getOrDefault(s.charAt(right),0)-1);
}
right++;
//检查条件,如果满足所有value 都不为正的说明,包含t的所有元素
while(checkCondition(needed)){
res=getMinString(res,s.substring(left,right));
if(needed.containsKey(s.charAt(left))){
needed.put(s.charAt(left),needed.get(s.charAt(left))+1);
}
left++;
flag=1;
}
}
if(flag==0)
res="";
return res;
}
public Boolean checkCondition(HashMap<Character,Integer> map){
for(Integer i:map.values()){
if(i>0)
return false;
}
return true;
}
public String getMinString(String a,String b){
return a.length()<b.length()? a:b;
}
长度最小的数组
题目描述
解题思想: 使用滑动窗口,满足条件后左指针左移。
代码实现
public int minSubArrayLen(int target, int[] nums) {
// 滑动窗口
// 滑动窗口
int n = nums.length;
int res = n+1;
int l=0,r=0;
while(r<n){
target-=nums[r];
while(target<=0){
if((r-l+1)<=res){
res = r-l+1;
}
target+=nums[l];
l++;
}
r++;
}
return res==n+1? 0:res;
}
找到字符串中所有字母异位词
滑动窗口 使用一个数组保存要匹配的窗口,window 要滑动的窗口。
public List<Integer> findAnagrams(String s, String p) {
//滑动窗口 使用一个数组保存要匹配的窗口
// window 要滑动的窗口
int n = s.length();
int m = p.length();
// 匹配的窗口
int [] needed = new int[26];
// 滑动的窗口
int [] window = new int[26];
char [] swords = s.toCharArray();
char [] pwords = p.toCharArray();
for(int i=0;i<m;i++){
needed[pwords[i]-'a']++;
}
int l=0,r=0;
List<Integer> res = new ArrayList<>();
while(r<n){
window[swords[r]-'a']++;
//因为是判断是否相等,不用if
if(r-l ==m-1){
if(checkwindows(needed,window)) {
res.add(l);
}
window[swords[l]-'a']--;
l++;
}
r++;
}
return res;
}
public boolean checkwindows(int [] needed,int[] window){
for(int i=0;i<26;i++){
if(needed[i]!=window[i]){
return false;
}
}
return true;
}
水果成篮
题目描述
代码实现:滑动窗口
// 维护left 和 right,每次取两个数作为基准,right 向右滑动
// 如果fruit[right] 不是篮子里面的值,更新l,当前的r的左边
public int totalFruit(int[] fruits) {
// 思想: 水果种类, 滑动窗口
// 维护left 和 right,每次取两个数作为基准,right 向右滑动
// 如果fruit[right] 不是篮子里面的值,更新l,当前的r的左边
int n = fruits.length;
int l = 0,r =0;
int sl=fruits[l],sr=fruits[r];
int res =1;
while(r<n){
if(fruits[r]==sl || fruits[r]==sr){
if(r-l+1>res){
res =r-l+1;
}
r++;
}else{
// 更新l,当前的r的左边
l = r-1;
sl = fruits[l];
sr = fruits[r];
// 存在 1 2 1 1 3 这种情况
while(l>=1 && fruits[l-1]==sl){
l--;
}
if(l-r+1>res){
res = l-r+1;
}
}
}
return res;
}
无重复的最长子串3
使用滑动窗口, r在 set 中,说明之前的满足条件, l 自增并删除set 。 使其不满足条件,最后将满足条件的放入。
public int lengthOfLongestSubstring(String s) {
// 直到遇到相同的删left
HashSet<Character> set = new HashSet<>();
int l = 0, r = 0;
int n = s.length();
char [] words = s.toCharArray();
int res = 0;
while(r < n){
char c = words[r];
while(set.contains(c)){
set.remove(words[l]);
l++;
}
res = Math.max(res, r - l + 1);
set.add(words[r]);
r++;
}
return res;
}
未完待续