image.png

解决思路

滑动窗口+哈希

本问题要求我们返回字符串S 中包含字符串T的全部字符的最小窗口。我们称包含TT的全部字母的窗口为可行窗口。

可以用简单的滑动窗口法来解决本问题。

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

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

答案是最小的可行窗口。

举个例子, S = “ABAACBAB”,T = “ABC”。则问题答案是 “ACB” ,下图是可行窗口中的一个。
image.png
算法

  1. 初始,left指针和right指针都指向S的第一个元素.

  2. 将 right 指针右移,扩张窗口,直到得到一个可行窗口,亦即包含T的全部字母的窗口。

  3. 得到可行的窗口后,将left 指针逐个右移,若得到的窗口依然可行,则更新最小窗口大小。

  4. 若窗口不再可行,则跳转至 2。

重复以上步骤,直到遍历完全部窗口。返回最小的窗口。

image.png
image.png

  1. class Solution {
  2. public String minWindow(String s, String t) {
  3. if (s.length() == 0 || t.length() == 0) {
  4. return "";
  5. }
  6. // Dictionary which keeps a count of all the unique characters in t.
  7. Map<Character, Integer> dictT = new HashMap<Character, Integer>();
  8. for (int i = 0; i < t.length(); i++) {
  9. int count = dictT.getOrDefault(t.charAt(i), 0);
  10. dictT.put(t.charAt(i), count + 1);
  11. }
  12. // Number of unique characters in t, which need to be present in the desired window.
  13. int required = dictT.size();
  14. // Left and Right pointer
  15. int l = 0, r = 0;
  16. // formed is used to keep track of how many unique characters in t
  17. // are present in the current window in its desired frequency.
  18. // e.g. if t is "AABC" then the window must have two A's, one B and one C.
  19. // Thus formed would be = 3 when all these conditions are met.
  20. int formed = 0;
  21. // Dictionary which keeps a count of all the unique characters in the current window.
  22. Map<Character, Integer> windowCounts = new HashMap<Character, Integer>();
  23. // ans list of the form (window length, left, right)
  24. int[] ans = {-1, 0, 0};
  25. while (r < s.length()) {
  26. // Add one character from the right to the window
  27. char c = s.charAt(r);
  28. int count = windowCounts.getOrDefault(c, 0);
  29. windowCounts.put(c, count + 1);
  30. // If the frequency of the current character added equals to the
  31. // desired count in t then increment the formed count by 1.
  32. if (dictT.containsKey(c) && windowCounts.get(c).intValue() == dictT.get(c).intValue()) {
  33. formed++;
  34. }
  35. // Try and co***act the window till the point where it ceases to be 'desirable'.
  36. while (l <= r && formed == required) {
  37. c = s.charAt(l);
  38. // Save the smallest window until now.
  39. if (ans[0] == -1 || r - l + 1 < ans[0]) {
  40. ans[0] = r - l + 1;
  41. ans[1] = l;
  42. ans[2] = r;
  43. }
  44. // The character at the position pointed by the
  45. // `Left` pointer is no longer a part of the window.
  46. windowCounts.put(c, windowCounts.get(c) - 1);
  47. if (dictT.containsKey(c) && windowCounts.get(c).intValue() < dictT.get(c).intValue()) {
  48. formed--;
  49. }
  50. // Move the left pointer ahead, this would help to look for a new window.
  51. l++;
  52. }
  53. // Keep expanding the window once we are done co***acting.
  54. r++;
  55. }
  56. return ans[0] == -1 ? "" : s.substring(ans[1], ans[2] + 1);
  57. }
  58. }