学习记录算法滑动窗口
TONG HUI滑动窗口算法代码框架
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| void slidingWindow(string s){ Map<Character,Integer> window = new HashMap(); int left = 0,right = 0; while(right < s.length()){ char c = s.charAt(right); right++; ... while(window needs shrink){ char d = s.charAt(left); left++; ... } } }
|
其中两处 ...
表示的更新窗口数据的地方,到时候写自己的逻辑。
这两个 ...
处的操作分别是扩大和缩小窗口的更新操作。
最小覆盖子串
滑动窗口算法的思路
- 我们在字符串
S
中使用双指针中的左右指针技巧,初始化 left = right = 0
,把索引左闭右开区间 [left, right)
称为一个「窗口
」。 - 我们先不断地增加
right
指针扩大窗口 [left, right)
,直到窗口中的字符串符合要求(包含了 T
中的所有字符)。 - 此时,我们停止增加
right
,转而不断增加 left
指针缩小窗口 [left, right)
,直到窗口中的字符串不再符合要求(不包含 T
中的所有字符了)。同时,每次增加 left
,我们都要更新一轮结果。 - 重复第
2
和第 3
步,直到 right
到达字符串 S
的尽头。
- 初始状态:
- 增加 right,直到窗口 [left, right) 包含了 T 中所有字符:
- 现在开始增加 left,缩小窗口 [left, right):
- 直到窗口中的字符串不再符合要求,left 不再继续移动:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| String minWindow(String s,String t){ if (s == null || s.length() == 0 || t == null ||t.length() == 0){ return ""; } Map<Character,Integer> window = new HashMap(); Map<Character,Integer> need = new HashMap(); for(int i=0;i<t.length();i++){ char c = t.charAt(i); if(need.containsKey(c)){ need.put(c,need.get(c)+1); }else{ need.put(c,1); } }
int left = 0,right = 0; int valid = 0; int start =0,len = Integer.MAX_VALUE; while(right < s.length()){ char c = s.charAt(right); right++; if(need.containsKey(c)){ if(window.containsKey(c)){ window.put(c,window.get(c)+1); }else{ window.put(c,1); } if(window.get(c).equals(need.get(c))){ valid++; } } while(valid == need.size()){ if(right - left < len){ start = left; len = right - left; } char d = s.charAt(left); left++; if(need.containsKey(d)){ if(window.get(d).equals(need.get(d))){ valid--; } window.put(d,window.get(d)-1); } } } return len == Integer.MAX_VALUE ? "" : s.substring(start,start+len); }
|
字符串排列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| boolean checkInclusion(String t,String s){ if (s1.length() > s2.length()) { return false; } Map<Character,Integer> window = new HashMap(); Map<Character,Integer> need = new HashMap(); for(int i=0;i<s1.length();i++){ char c = s1.charAt(i); if(need.containsKey(c)){ need.put(c,need.get(c)+1); }else{ need.put(c,1); } }
int left = 0,right = 0; int valid = 0; while(right < s2.length()){ char c = s2.charAt(right); right++; if(need.containsKey(c)){ if(window.containsKey(c)){ window.put(c,window.get(c)+1); }else{ window.put(c,1); } if(window.get(c).equals(need.get(c))){ valid++; } }
while(right - left == s1.length()){ if(valid == need.size()){ return true; } char d = s2.charAt(left); left++; if(need.containsKey(d)){ if(window.get(d).equals(need.get(d))){ valid--; } if(window.containsKey(d)){ window.put(d,window.get(d)-1); } } } } return false; }
|
- 移动
left
缩小窗口的时机是窗口大小大于 t.size()
时,因为排列,长度应该是一样的。 - 当
valid == need.size()
时,就说明窗口中就是一个合法的排列,所以立即返回 true。 - 由于这道题中
[left, right)
其实维护的是一个定长的窗口,窗口大小为 t.size()
。因为定长窗口每次向前滑动时只会移出一个字符,所以可以把内层的 while
改成 if
,效果是一样的。
找所有字母异位词
输入一个串 S,一个串 T,找到 S 中所有 T 的排列,返回它们的起始索引。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| List<Integer> findAnagrams(String s,String p){ if(p.length()>s.length()){ return new ArrayList(); } Map<Character,Integer> window = new HashMap(); Map<Character,Integer> need = new HashMap(); for(int i=0;i<p.length();i++){ char c = p.charAt(i); if(need.containsKey(c)){ need.put(c,need.get(c)+1); }else{ need.put(c,1); } } int left =0,right =0,valid =0; List<Integer> res = new ArrayList(); while(right < s.length()){ char c = s.charAt(right); right++; if(need.containsKey(c)){ if(window.containsKey(c)){ window.put(c,window.get(c)+1); }else{ window.put(c,1); } if(window.get(c).equals(need.get(c))){ valid++; } } while(right - left >= p.length()){ if(valid == need.size()){ res.add(left); } char d = s.charAt(left); left++; if(need.containsKey(d)){ if(window.get(d).equals(need.get(d))){ valid--; } window.put(d,window.get(d)-1); } } } return res; }
|
最长无重复子串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| int lengthOfLongestSubstring(String s){ Map<Character,Integer> window = new HashMap(); int left = 0,right =0,res =0; while(right < s.length()){ char c = s.charAt(right); right++; if(window.containsKey(c)){ window.put(c,window.get(c)+1); }else{ window.put(c,1); } while(window.get(c) > 1){ char d = s.charAt(left); left++; window.put(d,window.get(d)-1); } res = Math.max(res,right - left); } return res; }
|
当 window[c]
值大于 1
时,说明窗口中存在重复字符,不符合条件,就该移动 left
缩小窗口了嘛。