套用模板后,需要思考四个问题:
- 当right++时,即加入字符时,应该更新哪些数据?
- 什么条件下,窗口应该暂停扩大,left++,缩小窗口
- 当left++时,即移出字符时,应该更新哪些数据?
- 我们要的结果应该是在扩大窗口时更新还是缩小窗口时更新?
//滑动窗口算法的大致逻辑int letf = 0, right = 0;while (right < s.size()) {//增大窗口window.add(s[right]);right++;while (window need shrink) {//缩小窗口window.remove(s[left]);left++;}}/*其实困扰大家的,不是算法的思路,而是各种细节问题。比如说如何向窗口中添加新元素,如何缩小窗口,在窗口滑动的哪个阶段更新结果。即便你明白了这些细节,也容易出 bug,找 bug 还不知道怎么找,真的挺让人心烦的。*//* 滑动窗口算法框架 */void slidingWindow(String s, String t) {Map<Character, Integer> need, window;for (int i=0; i<t.length(); i++) {need.put(t.charAt(i), need.getOrDefault(t.charAt(i), 0) + 1);}int left = 0, right = 0;int valid = 0;while (right < s.length()) {// c是将移入窗口的字符char c = s.charAt(right);//右移窗口right++;//进行窗口数据的一系列更新.../*debug输出的位置*/System.out.print("window: " + left + ", " + right);//判断左侧窗口是否要收缩while (window need shrink) {//d是将移出窗口的字符char d = s.charAt(left);//左移窗口left++;//进行窗口数据的一系列更新...}}}
Leetcode76题:最小覆盖子串,难度hard
暴力解法,超出时间限制,思路就是暴力穷举每个包含t的子串,判断长度
class Solution {public String minWindow(String s, String t) {//暴力解法String ans = "";if (t.length() > s.length()) {return ans;}for (int i=0; i<s.length(); i++) {for (int j=i+1; j<=s.length(); j++) {String ss = s.substring(i, j); //s[i:j]Map<Character, Integer> map = new HashMap<>();for (int m=0; m<ss.length(); m++) {map.put(ss.charAt(m), map.getOrDefault(ss.charAt(m), 0)+1);}int k = 0;for (; k<t.length(); k++) {if (!map.containsKey(t.charAt(k)) || map.get(t.charAt(k)) <= 0) {break;} else {map.put(t.charAt(k), map.get(t.charAt(k))-1);}}if (k == t.length()) {if (ans.length() == 0) {ans = ss;} else {ans = ans.length() > ss.length() ? ss : ans;}}}}return ans;}}
滑动滑动窗口的思路
- 使用双指针的左右指针技巧:left = right = 0; [left, right)为一个窗口。
- 不断增加right指针扩大窗口,直到窗口中的字符串包含了T的所有字符。
- 停止right++,开始left++,缩小窗口,直到窗口中的字符串不再符合要求。
- 同时,每次增加left,就更新一轮结果。
重复2,3步,直到right到达字符串s的尽头。
public String minWindow(String s, String t) {String ans = "";Map<Character, Integer> need = new HashMap<>(); //需要包含字符和个数Map<Character, Integer> window = new HashMap<>(); //窗口中的字符和个数for (int i=0; i<t.length(); i++) {need.put(t.charAt(i), need.getOrDefault(t.charAt(i), 0) + 1);}//左右指针int left = 0, right = 0;int valid = 0; //窗口中有效字符个数while (right < s.length()) {char c = s.charAt(right); //移入窗口的元素right++;//进行窗口内数据的一系列更新if (need.containsKey(c)) {window.put(c, map.getOrDefault(c, 0) + 1);if (need.get(c).equals(window.get(c))) {valid++;}}//判断左侧是否收缩while (valid == need.size()) {if (ans.length() == 0) {ans = s.substring(left, right);} else {ans = ans.length() > (right-left) ? s.substring(left, right) : ans;}//移除的字符char d = s.charAt(left);left++;if (need.containsKey(d)) { //存在需要的map中if (need.get(d).euqals(window.get(d))) //个数也和window中相等valid--;window.put(d, map.getOrDefault(d, 0) - 1);}}return ans;}}
Leetcode567题:字符串排列,难度medium
Leetcode438题:找所有字母异位词,难度medium
Leetcode3题:最长无重复子串,难度medium
