[leetcode] MinimumWindowSubstring && SlidingWindowMaximum

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), 则表示我们找到一个解
          • 取最优解
          • l开始向r靠近,更新最优解。
    • 方法二:利用一个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){
                    // 需要当前char
                    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; // 用来存储num的坐标
        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;

    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值