双指针:滑动窗口 滑动窗口框架:
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 void slidingWindow (string s, string t) { unordered_map<char , int > need, window; for (char c : t) need[c]++; int left = 0 , right = 0 ; int valid = 0 ; while (right < s.size ()) { char c = s[right]; right++; ... printf ("window: [%d, %d)\n" , left, right); while (window needs shrink) { char d = s[left]; left++; ... } } }
最小覆盖子串 https://leetcode-cn.com/problems/minimum-window-substring/
右指针一直前进,知道找齐所有的字符;找齐字符后再收缩左指针,尽量取最短的子串。
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 func minWindow (s string , t string ) string { window := make (map [byte ]int ) need := make (map [byte ]int ) for i := 0 ; i < len (t); i++ { need[t[i]] += 1 } left, right := 0 , 0 valid := 0 start, minLen := 0 , math.MaxInt32 for right < len (s) { c := s[right] right++ if val, ok := need[c]; ok { window[c]++ if window[c] == val { valid++ } } for valid == len (need) { if right - left < minLen { minLen = right - left start = left } d := s[left] left++ if val, ok := need[d]; ok { if window[d] == val { valid-- } window[d]-- } } } if minLen == math.MaxInt32 { return "" } else { return s[start : start+minLen] } }
字符串的排列 https://leetcode-cn.com/problems/permutation-in-string/
还是类似的思路
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 func checkInclusion (s1 string , s2 string ) bool { window, need := make (map [byte ]int ), make (map [byte ]int ) for i := 0 ; i < len (s1); i++ { need[s1[i]]++ } left, right := 0 , 0 valid := 0 for right < len (s2) { c := s2[right] right++ if val, ok := need[c]; ok { window[c]++ if window[c] == val { valid++ } } for right - left >= len (s1) { if valid == len (need) { return true } d := s2[left] left++ if val, ok := need[d]; ok { if window[d] == val { valid-- } window[d]-- } } } return false }
找到字符串中所有字母异位词 https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/
一样的题,不过是要输出字符串的开始位置。
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 func findAnagrams (s string , p string ) []int { window, need := make (map [byte ]int ), make (map [byte ]int ) for i := 0 ; i < len (p); i++ { need[p[i]]++ } left, right := 0 , 0 valid := 0 res := make ([]int , 0 ) for right < len (s) { c := s[right] right++ if val, ok := need[c]; ok { window[c]++ if window[c] == val { valid++ } } for right - left >= len (p) { if valid == len (need) { res = append (res, left) } d := s[left] left++ if val, ok := need[d]; ok { if window[d] == val { valid-- } window[d]-- } } } return res }
无重复字符的最长子串 https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/
这题还要更简单一些,无重复字串直接查看窗口内的哈希表就好了,一旦右指针前进使得有字符重复就收缩左指针,直到没有重复位置,再计算新的长度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func lengthOfLongestSubstring (s string ) int { window := make (map [byte ]int ) left, right := 0 , 0 maxLen := 0 for right < len (s) { c := s[right] right++ window[c]++ for window[c] > 1 { d := s[left] left++ window[d]-- } if right - left > maxLen { maxLen = right - left } } return maxLen }