【道德经·第九章】 持而盈之,不如其已。揣(chuǎi)而锐之,不可长保。金玉满堂,莫之能守。富贵而骄,自遗(yí)其咎。功成身退,天之道。
 
 
算法模板框架 
滑动窗口算法的思路非常简单,就是维护一个窗口,不断滑动,然后更新答案。这里总结一套滑动窗口算法的通用模板: 来源:labuladong的算法小抄
 
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 void  SlideWindow (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++;                          ...         }     } } 
 
leetcode题目 
给定一个含有 n 个正整数的数组和一个正整数target。找出该数组中满足其和 ≥ target 的长度最小的连续子数组并返回其长度。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int  minSubArrayLen (int  target, vector<int >& nums)   {    int  l = 0 , r = 0 , sum = 0 , ans = INT_MAX;     while (r < nums.size ()) {         sum += nums[r];         r++;         while (sum >= target) {             ans = min (ans, r-l);             sum -= nums[l];             l++;         }     }     return  ans==INT_MAX ? 0  : ans; } 
 
给你一个数组nums和一个整数k,返回子数组内所有元素的乘积严格小于k的连续子数组的数目。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int  numSubarrayProductLessThanK (vector<int >& nums, int  k)   {    int  l = 0 , r = 0 , p = 1 , ans = 0 ;     while (r < nums.size ()) {         p = p*nums[r];         r++;         while (p >= k) {             p = p/nums[l];             l++;             if (l == r) break ;         }         ans += (r>l ? r-l : 0 );     }     return  ans; } 
 
给你一个字符串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 string minWindow (string s, string t)   {    unordered_map<char , int > need, window;     for (char  c : t) need[c]++;     int  left = 0 , right = 0 , valid = 0 , start = 0 , minlen = INT_MAX;     while (right < s.size ()) {         char  c = s[right];         right++;         if (need.count (c)) {             window[c]++;             if (window[c] == need[c])                  valid++;         }         while (valid >= need.size ()) {             if (right-left < minlen) {                 minlen = right-left;                 start = left;             }             char  d = s[left];             left++;             if (need.count (d)) {                 if (window[d] == need[d])                      valid--;                 window[d]--;             }         }     }     return  minlen==INT_MAX ? ""  : s.substr (start, maxlen); } 
 
给定一个字符串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 class  Solution  {public :    int  lengthOfLongestSubstringTwoDistinct (string s)   {         unordered_map<char , int > window;         int  cnt = 0 ;                                  int  l = 0 , r = 0 ;             int  maxLen = 0 ;         while (r < s.size ()){             if (window[s[r]] == 0 )                 cnt ++;                                       window[s[r]]++;                     r++;             while (cnt > 2 ){                          window[s[l]]--;                        l++;                                  if (window[s[l]] == 0 ){                     cnt --;                          }             }             maxLen = max (maxLen, r-l);          }         return  maxLen;     } }; 
 
给你两个字符串s1和s2,写一个函数来判断s2是否包含s1的排列。
 
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 bool  checkInclusion (string s1, string s2)   {    unordered_map<char , int > need, window;     for (char  c : s1) need[c]++;     int  left = 0 , right = 0 , valid = 0 ;     while (right < s2.size ()) {         char  c = s2[right];         right++;         if (need.count (c)) {             window[c]++;             if (window[c] == need[c])                 valid++;         }         if (right-left >= s1.size ()) {             if (valid == need.size ())                  return  true ;                          char  d = s2[left];             left++;             if (need.count (d)) {                 if (window[d] == need[d])                      valid--;                 window[d]--;             }         }     }     return  false ; } 
 
给定两个字符串s和p,找到s中所有p的异位词的子串,返回这些子串的起始索引。
 
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 vector<int > findAnagrams (string s, string p)   {    unordered_map<char , int > need, window;     for (char  c : p) need[c]++;     int  left = 0 , right = 0 , valid = 0 ;     vector<int > res;     while (right < s.size ()) {         char  c = s[right];         right++;         if (need.count (c)) {             window[c]++;             if (window[c] == need[c])                 valid++;         }         while (right-left >= p.size ()) {             if (valid == need.size ())                 res.push_back (left);             char  d = s[left];             left++;             if (need.count (d)) {                 if (window[d] == need[d])                     valid--;                 window[d]--;             }         }     }     return  res; } 
 
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串的长度.
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int  lengthOfLongestSubstring (string s)   {    unordered_map<char , int > window;     int  left = 0 , right = 0 , res = 0 ;     while (right < s.size ()) {         char  c = s[right];         right++;         window[c]++;         while (window[c] > 1 ) {             char  d = s[left];             left ++;             if (window.count (d))                  window[d]--;         }         res = max (res, right-left);     }     return  res; } 
 
给你一个字符串 s 和一个整数 k ,请你找出 s 中的最长子串, 要求该子串中的每一字符出现次数都不少于 k 。返回这一子串的长度。
 
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 59 class  Solution  {public :    int  longestSubstring (string s, int  k)   {         int  n = s.length ();         if  (k == 1 ) {             return  n;         }                  if  (k > n) {             return  0 ;         }                  int  res = 0 ;         for  (int  kind = 1 ; kind <= 26 ; kind++) {             res = max (getRes (s, k, kind), res);         }                  return  res;     }               int  getRes (string s, int  k, int  kind)   {         int  n = s.length ();                  int  res = 0 ;         vector<int > cnt (26 )  ;                  int  left = 0 , right = 0 ;         int  sum = 0 ;           int  total = 0 ;           while  (right < n) {             int  indexR = s[right] - 'a' ;             cnt[indexR]++;             if  (cnt[indexR] == 1 ) {                 sum++;             }             if  (cnt[indexR] == k) {                 total++;             }             while  (sum > kind) {                 int  indexL = s[left] - 'a' ;                 cnt[indexL]--;                 if  (cnt[indexL] == 0 ) {                     sum--;                 }                 if  (cnt[indexL] == k-1 ) {                     total--;                 }                 left++;             }             if  (sum == total) {                 res = max (res, right-left+1 );             }             right++;         }         return  res;     } }; 
 
小结 滑动窗口算法的思路是这样: 1.我们在字符串 S 中使用双指针中的左右指针技巧,初始化 left = right = 0,把索引左闭右开区间 [left, right) 称为一个「窗口」。 2.我们先不断地增加 right 指针扩大窗口 [left, right),直到窗口中的字符串符合要求(包含了 T 中的所有字符)。 3.此时,我们停止增加 right,转而不断增加 left 指针缩小窗口 [left, right),直到窗口中的字符串不再符合要求(不包含 T 中的所有字符了)。同时,每次增加 left,我们都要更新一轮结果。 4.重复第 2 和第 3 步,直到 right 到达字符串 S 的尽头。 这个思路其实也不难,第 2 步相当于在寻找一个可行解 ,然后第 3 步在优化这个可行解 ,最终找到最优解,也就是最短的覆盖子串。左右指针轮流前进,窗口大小增增减减,窗口不断向右滑动,这就是「滑动窗口」这个名字的来历。
位我上者,灿烂星空;道德律令,在我心中。