滑动窗口系列题

滑动窗口总体框架

// 判断 s 中是否存在 t 的排列
boolean checkInclusion(string t, string s) {
    int[] needMap, windowMap;
    for (int i = 0; i < lenP; i++) {//映射
        needMap[p.charAt(i)]++;
    }
    for (int i = 0; i < needMap.length; i++) { //字符种类
            if (needMap[i] != 0) needSize++;
    }
    int left = 0, right = 0;
    int valid = 0;
    while (right < lenS) {
        char c = s[right];
        
        // 进行窗口内数据的一系列更新
        if (needMap(c)) {
            window[c]++;
            if (window[c] == need[c])
                valid++;
        }

        // 判断左侧窗口是否要收缩
        while (right - left + 1 >= lenS) {
            // 在这里判断是否找到了合法的子串
            if (valid == needSize)
                return true;
            char d = s[left];
            left++;
            // 进行窗口内数据的一系列更新
            if (needMap[d]) {
                if (windowMap[d] == needMap[d])
                    valid--;
                window[d]--;
            }
        }
        right++;
    }
    // 未找到符合条件的子串
    return false;
}

参考:https://leetcode.cn/problems/find-all-anagrams-in-a-string/solutions/9749/hua-dong-chuang-kou-tong-yong-si-xiang-jie-jue-zi-/

滑动窗口也叫做「虫取法」,非常生动形象。因为滑动窗口的两个指针移动的过程和虫子爬动的过程非常像:前脚不动,把后脚移动过来;后脚不动,把前脚向前移动

以右指针作为驱动,拖着左指针向前走。右指针每次只移动一步,而左指针在内部 while 循环中每次可能移动多步。右指针是主动前移,探索未知的新区域;左指针是被迫移动,负责寻找满足题意的区间。
参考:https://leetcode.cn/problems/max-consecutive-ones-iii/solutions/609055/fen-xiang-hua-dong-chuang-kou-mo-ban-mia-f76z/

209. 长度最小的子数组

public int minSubArrayLen(int target, int[] nums) {
    int startIdx = 0;
    int sum = 0;
    int len = nums.length;
    int minLen = len+1;
    for (int i = 0; i < len; i++) {
        sum += nums[i];
        while (sum >= target) {
            minLen = Math.min(minLen, i - startIdx + 1);
            sum -= nums[startIdx++];
        }
    }
    return minLen == len+1 ? 0 : minLen;
}

904. 水果成篮

public int totalFruit(int[] fruits) {
    //使用map速度太慢 用数组进行优化
    int len = fruits.length;
    int[] map = new int[len + 1];
    int startIdx = 0;
    int maxLen = 0;
    int realCount = 0; //此时用了几种
    for (int i = 0; i < len; i++) {
        if(++map[fruits[i]] == 1) realCount++;
        while (realCount > 2) {
            int temp = fruits[startIdx++];
            if(--map[temp]  == 0) realCount--;
        }
        maxLen = Math.max(maxLen, i - startIdx + 1);
    }
    return maxLen;
}

76. 最小覆盖子串

public String minWindow(String s, String t) {
    int lenS = s.length();
    int lenT = t.length();
    int[] needMap = new int[128]; //大小写都有  需要的字符映射
    int[] windowMap = new int[128]; //滑动窗口中的数据映射
    for (int i = 0; i < lenT; i++) {
        needMap[t.charAt(i)]++;
    }
    //真正需求的种类数 因为在下面是用t中每种字符的个数与窗口中对应字符的个数相等之后再去做进一步的操作
    int needSize = 0;
    for (int i = 0; i < 128; i++) {
        if(needMap[i] != 0) needSize++;
    }
    int startIdx = 0, rightIdx = 0; //滑动窗口的起始点和终点
    int valid = 0;
    int minLen = lenS + 1;
    int ans = 0;//保留最小子串的起点
    while (rightIdx < lenS) {
        char rightChar = s.charAt(rightIdx);
        if (needMap[rightChar] != 0) { //它是需要的字符
            windowMap[rightChar]++; //进来窗口
            //当窗口中属于所需要字符的个数达到要求
            if (windowMap[rightChar] == needMap[rightChar]) {
                valid++;
            }
        }
        //满足了T中的所有字符 即找到了一个可行解 现在窗口收缩 找最优解
        while (valid == needSize) {
            if (rightIdx - startIdx + 1 < minLen) {
                minLen = rightIdx - startIdx + 1;
                ans = startIdx;
            }
            char leftChar = s.charAt(startIdx);
            startIdx++; //往后移动 窗口缩小
            if (needMap[leftChar] != 0) { //即将移除的字符是t中的字符
                //如果即将移除的字符个数和t中的对应 例如"AABC" "ABC"
                //在移除第二个A时 合法性就要-1
                if(windowMap[leftChar] == needMap[leftChar]){
                    valid--;
                }
                //顺序一定不能反了
                windowMap[leftChar]--; //从窗口中移除
            }
        }
        rightIdx++; //窗口增大 继续寻找可行解
    }
    return minLen == lenS + 1 ? "" : s.substring(ans, ans + minLen);
}

643. 子数组最大平均数 I

public double findMaxAverage(int[] nums, int k) {
    int left = 0;
    double average = 0;
    double sum = 0;
    for (int right = 0; right < nums.length; right++) {
        sum += nums[right];
        k--;
        if (k == 0) {
            sum -= nums[left];
            left++;
            average = Math.max(sum, average);
        }
    }
    return average / k;
}

3. 无重复字符的最长子串

public int lengthOfLongestSubstring(String s) {
    int[] map = new int[128];
    char[] arr = s.toCharArray();
    int len = arr.length;
    int startIdx = 0, rightIdx = 0;
    int ans = 0;
    while (rightIdx < len) {
        //以pwwkew为例子 遍历到pww时候 应该退回到 w 此时起始点和终点都在第二个w
        if (++map[arr[rightIdx]] > 1) {
            while (map[arr[rightIdx]] > 1) {
                map[arr[startIdx]]--;
                startIdx++;
            }
        } 
        ans = Math.max(ans, rightIdx - startIdx + 1);
        rightIdx++;
    }
    return ans;
}

438.找到字符串中所有字母异位词

public List<Integer> findAnagrams(String s, String p) {
    int lenP = p.length();
    int lenS = s.length();
    List<Integer> ans = new ArrayList<>();
    int[] needMap = new int[128];
    int[] windowMap = new int[128];
    for (int i = 0; i < lenP; i++) {
        needMap[p.charAt(i)]++;
    }
    int needSize = 0; //p中的字符种类数
    for (int i = 0; i < 128; i++) {
        if (needMap[i] != 0) needSize++;
    }
    int startIdx = 0, rightIdx = 0;
    int valid = 0;
    while (rightIdx < lenS) {
        char c = s.charAt(rightIdx);
        if (needMap[c] > 0) {
            windowMap[c]++;
            if (needMap[c] == windowMap[c]) {
                valid++;
            }
        }
        // 判断左侧窗口是否要收缩
        while (rightIdx - startIdx + 1 >= lenP) {
            if(valid == needSize){
                ans.add(startIdx);
            }
            char d = s.charAt(startIdx);
            startIdx++;
            if (needMap[d] != 0) {
                if (needMap[d] == windowMap[d]) {
                    valid--;
                }
                windowMap[d]--;
            }
        }
        rightIdx++;
    }
    return ans;
}

567. 字符串的排列

public boolean checkInclusion(String s1, String s2) {
    int lenS1 = s1.length();
    int lenS2 = s2.length();
    int[] needMap = new int[128];
    int[] windowMap = new int[128];
    for (int i = 0; i < lenS1; i++) {
        needMap[s1.charAt(i)]++;
    }
    int needSize = 0;
    for (int i = 0; i < 128; i++) {
        if (needMap[i] != 0) needSize++;
    }
    int left = 0, right = 0;
    int valid = 0;
    while (right < lenS2) {
        char c = s2.charAt(right);
        if (needMap[c] != 0) {
            windowMap[c]++;
            if (windowMap[c] == needMap[c]) {
                valid++;
            }
        }
        while (right - left + 1 >= lenS1) {
            if (valid == needSize) {
                return true;
            }
            char d = s2.charAt(left);
            left++;
            if (needMap[d] != 0) {
                if (windowMap[d] == needMap[d]) {
                    valid--;
                }
                windowMap[d]--;
            }
        }
        right++;
    }
    return false;
}

1004. 最大连续1的个数 III

public int longestOnes(int[] nums, int k) {
    //题目翻译为:子数组内最多k个0
    int len = nums.length;
    int left = 0, right = 0;
    int ans = 0;
    int count = 0;
    while (right < len) {
        if (nums[right] == 0 ) {
            count++;
        }
        while (count > k){
            if(nums[left] == 0) count--;
            left++;
        }
        right++;
        ans = Math.max(ans, right - left);
    }
    return ans;
}

1208.尽可能使字符串相等

public int equalSubstring(String s, String t, int maxCost) {
    char[] arr1 = s.toCharArray();
    char[] arr2 = t.toCharArray();
    int len1 = arr1.length;
    int ans = 0;
    int[] cost = new int[len1];
    for (int i = 0; i < cost.length; i++) {
        cost[i] = Math.abs(arr1[i] - arr2[i]);
    }
    int left = 0, right = 0;
    int sum = 0;
    while (right < len1) {
        sum += cost[right];
        while (sum > maxCost) {
            sum -= cost[left];
            left++;
        }
        ans = Math.max(ans, right - left + 1);
        right++;
    }
    return ans;
}

1052. 爱生气的书店老板

public int maxSatisfied(int[] customers, int[] grumpy, int minutes) {
    int len = customers.length;
    int left = 0, right = 0;
    int sum = 0;
    int max = 0;
    int ans = 0;
    // 不生气的对应的顾客数直接加上 并赋值为0
    for (int i = 0; i < customers.length; i++) {
        if (grumpy[i] == 0) {
            sum += customers[i];
            customers[i] = 0;
        }
    }
    //现在从顾客数组中找到连续的minutes个数的最大值
    int count = 0;
    while (right < len) {
        max += customers[right];
        count++;
        if (count > minutes) {
            max -= customers[left];
            left++;
            count--;
        }
        ans = Math.max(ans, max);
        right++;
    }
    return sum + ans;

}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值