MinimumWindowSubstring
- 问题描述:给定一个字符串A和字符串B,在A中找到最短的一个连续子字符串,使其包括B中的所有字母。比如说:A=‘ABCDEFG’, B = ‘EG’, 则最短的子字符串是EFG。
- 解决思路
- 方法一:利用两个map
- 首先,我们把B中所有字母出现的次数保存成一个map T类型的数据(没有非法输入的情况下可以用128纬的vector来提高效率)。
- 然后我们再用一个map S来保存已经找到的字符串。
- 用l和r表示两个指针,代表子字符串的头和尾。
- 从头到尾遍历字符串A(r逐步向后走)
- 如果当前字符在B中,S[c] += 1
- if S[c] <= T[c]: find += 1
- 如果find == len(B), 则表示我们找到一个解
- 方法二:利用一个map
- 同样有两个指针l和r
- map T一开始初始化为B中每个字符出现的次数
- counter = len(B) 表示我们要找的字符个数
- 从头到尾遍历字符串A
- if T[c] > 0: counter --;
- T[c] --; 注意,即时c没在B中出现过,这里也要执行减法操作。目的是为了区别A中独有的字符和B中的字符
- 当counter == 0的时候
- 我们逐步增加l,使其靠近r
- 对于l位置的c
- 如果T[c] == 0, 表示c是B中的字符,应该counter += 1
- T[c] += 1
- 注意,当A中的字母次数比B中多,l在靠近r的时候,删除一个字符并不影响。这是通过T[c]–和T[c]++来完成的。
- 代码:
string minWindow(string s, string t) {
string res = "";
vector<int > target_map(128, 0);
int target_num = (int) t.length();
for(int i=0;i<t.length();i++){
target_map[t[i]] += 1;
}
unordered_map<char, int > source_map;
int l = 0;
int r = 0;
int find_char = 0;
while(true){
if(l==r && r >= s.length())
break;
if(find_char < target_num){
if(r >= s.length())
break;
char c = s[r];
if(target_map[c]!=0){
if(target_map[c] > source_map[c]){
find_char += 1;
}
source_map[c] += 1;
}
r += 1;
}else{
if(find_char == target_num){
string cur = s.substr(l, (r-l));
if(res == "" || res.length() > cur.length()){
res = cur;
}
char c = s[l];
if(source_map[c] > 0){
source_map[c] -= 1;
if(source_map[c] < target_map[c]){
find_char -= 1;
}
}
l += 1;
}
}
}
return res;
}
string minWindowV2(string s, string t){
vector<int> map(128, 0);
for(auto c: t)
map[c] += 1;
int l = 0, r = 0, size = (int) s.length(), len = -1, head = 0;
int counter = (int) t.length();
while(r < size){
if(map[s[r]] > 0){
counter -= 1;
}
map[s[r]] -= 1;
r += 1;
while (counter == 0){
if(map[s[l]] == 0){
counter += 1;
if(len == -1 || (r - l) < len){
len = r - l;
head = l;
}
}
map[s[l]] += 1;
l += 1;
}
}
return len == -1 ? "": s.substr(head, len);
}
SlidingWindowMaximum
- 问题描述:给定一个数组A,和一个滑窗大小K。返回一个新的数组,数组里面是连续滑动窗口,每个窗口的最大值。比如说: A=[1, 3, 5, 2, 1, 0], return = [5, 5, 5, 2]。
- 解决思路:
- 暴力:两重循环,第一个由0~A.size()-k, 第二重由0~k(负责计算窗口内的最大数)
- 利用优先队列(最大)
- 我们新定义一个数据类型Node(val, pos)
- 从头到尾开始遍历A
- 每次都构建一个Node,加入优先队列中
- 如果i >= (k-1) 表示我们要开始添加最大值到vector中了。
- 弹出第一个合格的node,合格的node是指pos距离i不超过k。
- 这个node的val就是我们要加入的值
- 利用双向队列
- 定义一个双向队列D,用来存储数字对应的下标
- 从头到尾遍历A, i∈[0,n)i \in [0, n)i∈[0,n)
- 如果D的第一个元素e<=i-k,则说明e是非法的,弹出
- 接下来是从D的尾找到头,找到第一个大于nums[i],将其他元素的弹出。因为这些元素的感受野内的点也在i的感受耶内,而nums[i]大于他们,所以他们就没有存在的必要。
- 如果i >= (k-1),要将一个值加入max value了。
- 我们弹出nums[D.front()]加入结果中去
- 相当于我们构建了一个max的队列,每个都可得到队列的最大值。
- 代码:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> res;
priority_queue<Node, vector<Node>> queue1;
for(int i=0;i<nums.size();i++){
queue1.push(Node(i, nums[i]));
if(i >= (k-1)){
while(true){
Node node = queue1.top();
if(abs(i-node.pos) >= k){
queue1.pop();
continue;
}else{
res.push_back(node.v);
break;
}
}
}
}
return res;
}
vector<int> maxSlidingWindowV2(vector<int>& nums, int k){
vector<int> res;
deque<int> deque1;
for(int i=0;i<nums.size();i++){
if(!deque1.empty() && deque1.front() <= (i-k))
deque1.pop_front();
while(!deque1.empty() && nums[deque1.back()] < nums[i])
deque1.pop_back();
deque1.push_back(i);
if(i >= (k-1)){
res.push_back(nums[deque1.front()]);
}
}
return res;
}