76.最小覆盖子串

给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。

示例

输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"

说明

  • 如果 S 中不存这样的子串,则返回空字符串 “”。
  • 如果 S 中存在这样的子串,我们保证它是唯一的答案。

解法

滑动窗口

在滑动窗口类型的问题中都会有两个指针。一个用于延伸现有窗口的 right 指针,和一个用于收缩窗口的 left 指针。在任意时刻,只有一个指针运动,而另一个保持静止。

本题的解法很符合直觉。我们通过移动right指针不断扩张窗口。当窗口包含全部所需的字符后,如果能收缩,我们就收缩窗口直到得到最小窗口。

举个例子, S = "ABAACBAB",T = "ABC"。则问题答案是 “ACB” ,下图是可行窗口中的一个。
在这里插入图片描述

算法

  1. 初始,left 指针和 right指针都指向 S 的第一个元素
  2. 将 right 指针右移,扩张窗口,直到得到一个可行窗口,亦即包含TT的全部字母的窗口。
  3. 得到可行的窗口后,将 left 指针逐个右移,若得到的窗口依然可行,则更新最小窗口大小。
  4. 若窗口不再可行,则跳转至 2

在这里插入图片描述
重复以上步骤,直到遍历完全部窗口。返回最小的窗口。

在这里插入图片描述

//Java
public String minWindow(String s, String t) {
      if (s.length() == 0 || t.length() == 0) return "";
      // Dictionary which keeps a count of all the unique characters in t.
      Map<Character, Integer> dictT = new HashMap<Character, Integer>();
      for (int i = 0; i < t.length(); i++) {
          int count = dictT.getOrDefault(t.charAt(i), 0);
          dictT.put(t.charAt(i), count + 1);
      }
      // Number of unique characters in t, which need to be present in the desired window.
      int required = dictT.size();
      // Left and Right pointer
      int l = 0, r = 0;
      // formed is used to keep track of how many unique characters in t
      // are present in the current window in its desired frequency.
      // e.g. if t is "AABC" then the window must have two A's, one B and one C.
      // Thus formed would be = 3 when all these conditions are met.
      int formed = 0;
      // Dictionary which keeps a count of all the unique characters in the current window.
      Map<Character, Integer> windowCounts = new HashMap<Character, Integer>();
      // ans list of the form (window length, left, right)
      int[] ans = {-1, 0, 0};
      while (r < s.length()) {
          // Add one character from the right to the window
          char c = s.charAt(r);
          int count = windowCounts.getOrDefault(c, 0);
          windowCounts.put(c, count + 1);
          // If the frequency of the current character added equals to the
          // desired count in t then increment the formed count by 1.
          if (dictT.containsKey(c) && windowCounts.get(c).intValue() == dictT.get(c).intValue()) {
              formed++;
          }
          // Try and co***act the window till the point where it ceases to be 'desirable'.
          while (l <= r && formed == required) {
              c = s.charAt(l);
              // Save the smallest window until now.
              if (ans[0] == -1 || r - l + 1 < ans[0]) {
                  ans[0] = r - l + 1;
                  ans[1] = l;
                  ans[2] = r;
              }
              // The character at the position pointed by the
              // `Left` pointer is no longer a part of the window.
              windowCounts.put(c, windowCounts.get(c) - 1);
              if (dictT.containsKey(c) && windowCounts.get(c).intValue() < dictT.get(c).intValue()) {
                  formed--;
              }
              // Move the left pointer ahead, this would help to look for a new window.
              l++;
          }
          // Keep expanding the window once we are done co***acting.
          r++;   
      }
      return ans[0] == -1 ? "" : s.substring(ans[1], ans[2] + 1);
  }
//C#
public string MinWindow(string s, string t)
        {
            if (s.Length * t.Length == 0) return "";
            Dictionary<char,int> dic = new Dictionary<char, int>();
            Dictionary<char,int> windowCounts = new Dictionary<char, int>();
            int required = dic.Count;
            int left = 0;
            int right = 0;
            int formed = 0;
            int[] ans = {-1, 0, 0};//window length, left, right

            for (int i = 0; i < t.Length; i++)//将t中的(字符,个数)添加至字典
            {
                int count = dic.ContainsKey(t[i]) ? dic[t[i]] : 0;
                dic.Add(t[i], count + 1);
            }
            while (right < s.Length)
            {
                char c = s[right];
                int count = windowCounts.ContainsKey(c) ? windowCounts[c] : 0;
                windowCounts.Add(c, count + 1);
                if (dic.ContainsKey(c) && windowCounts[c] == dic[c])
                {
                    formed++;
                }
                while (left < right || formed == required)
                {
                    c = s[left];
                    if (ans[0] == -1 || right - left < ans[0])
                    {
                        ans[0] = right - left + 1;
                        ans[1] = left;
                        ans[2] = right;
                    }
                    windowCounts.Add(c, windowCounts[c] - 1);
                    if (dic.ContainsKey(c) && windowCounts[c] < dic[c])
                    {
                        formed--;
                    }
                    left++;
                }
                right++;
            }
            return ans[0] == -1 ? "" : s.Substring(ans[1], ans[2] + 1);
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值