微软面试题: LeetCode 76. 最小覆盖子串 出现次数:2
题目描述:
题解:
滑动窗口思想:用 i, j 表示滑动窗口的左边界和右边界,通过改变i,j来扩展和收缩滑动窗口,可以想象成一个窗口在字符串上游走,
当这个窗口包含的元素满足条件,即包含字符串T的所有元素,记录下这个滑动窗口的长度j-i+1 和 窗口其实位置 i ,这些长度中的最小值
对应的子串 就是要求的结果。设置一个变量 cnt 记录 滑动窗口中
扩展滑动窗口 :向右 右边界移动 j 直到 窗口中的子串恰好覆盖 t 中所有字符时停止。(从不覆盖到覆盖)
收缩滑动窗口: 向右移动左边界 i 直到 窗口中的子串恰好覆盖 t 中所有字符时停止。(再移动一步就不再覆盖,此时保存最小子串)
代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 class Solution { 4 public: 5 string minWindow(string s, string t) 6 { 7 unordered_map<char,int> need;//记录 字符串t中的字符和出现的次数 8 unordered_map<char,int> window;// 记录滑动窗口内的字符和出现的次数 9 for(int i = 0; i < t.size();++i) 10 { 11 need[t[i]]++; 12 } 13 const int t_cnt = need.size(); 14 int start = 0;//s中包含t中所有字符的最短子串起始下标 15 int len = INT_MAX;//最短子串长度 16 int cnt = 0;//t中的字符 在滑动窗口中已完备存在的个数 17 int left = 0,right = 0;//滑动窗口左右边界 18 char c = ' '; 19 while (right < s.size()) 20 { 21 //移动 right 扩大窗口,到窗口中的子串恰好覆盖 t 中所有字符时停止 22 while (cnt < t_cnt && right < s.size()) 23 { 24 c = s[right]; 25 if(need.find(c) != need.end()) 26 { 27 if(++window[c] == need[c]) 28 { 29 ++cnt; 30 } 31 } 32 ++right; 33 } 34 //right 移动到最右,滑动窗口内的子串仍然没有覆盖t中所有字符,直接返回之前得到的最小覆盖子串 35 if(right == s.size() && cnt < t_cnt) 36 { 37 return len == INT_MAX?"":s.substr(start,len); 38 } 39 //移动left,收缩窗口,到窗口内的子串恰好覆盖 t 中所有字符时停止(再收缩一步就不再覆盖了) 40 while(left < right && cnt == t_cnt) 41 { 42 c = s[left]; 43 if(need.find(c) != need.end()) 44 { 45 if(window[c]-- == need[c]) 46 { 47 --cnt; 48 } 49 } 50 ++left; 51 } 52 //此时的最小覆盖子串的范围应该是 [left-1,right-1] 53 if(right - left + 1 < len ) 54 { 55 start = left - 1; 56 len = right - left + 1; 57 } 58 } 59 return len == INT_MAX?"":s.substr(start,len); 60 } 61 62 };