这道题在LeetCode上的难度标签为:困难!你能看出来这是一道用双指针就可以解决的题目吗?
LeetCode链接:76. 最小覆盖子串
1.题目描述
给你一个字符串 s
、一个字符串 t
。返回 s
中涵盖 t
所有字符的最小子串。如果 s
中不存在涵盖 t
所有字符的子串,则返回空字符串 ""
。
注意:
- 对于
t
中重复字符,我们寻找的子字符串中该字符数量必须不少于 t
中该字符数量。
- 如果
s
中存在这样的子串,我们保证它是唯一的答案。
示例 1:
1 2 3
| 输入:s = "ADOBECODEBANC", t = "ABC" 输出:"BANC" 解释:最小覆盖子串 "BANC" 包含来自字符串 t 的 'A'、'B' 和 'C'。
|
示例 2:
1 2 3
| 输入:s = "a", t = "a" 输出:"a" 解释:整个字符串 s 是最小覆盖子串。
|
示例 3:
1 2 3 4
| 输入: s = "a", t = "aa" 输出: "" 解释: t 中两个字符 'a' 均应包含在 s 的子串中, 因此没有符合条件的子字符串,返回空字符串。
|
提示:
m == s.length
n == t.length
- $1 <= m, n <= 10^5$
s
和 t
由英文字母组成
进阶:你能设计一个在 o(m+n)
时间内解决此问题的算法吗?
2.题解
2.1 暴力解法(超时)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| import java.util.HashMap; import java.util.Map;
class Solution { public String minWindow(String s, String t) { Map<Character, Integer> map = new HashMap<>(); for (int i = 0; i < t.length(); i++) { char c = t.charAt(i); map.put(c, map.getOrDefault(c, 0) + 1); }
int minLen = Integer.MAX_VALUE; int left = 0, right = 0; int length = 0; String result = "";
for (left = 0; left < s.length(); left++) { Map<Character, Integer> countMap = new HashMap<>(map);
for (right = left; right < s.length(); right++) { char c = s.charAt(right); if (countMap.containsKey(c)) { countMap.put(c, countMap.get(c) - 1); if (countMap.get(c) == 0) { countMap.remove(c); } }
if (countMap.size() == 0) { length = right - left + 1; if (length < minLen) { minLen = length; result = s.substring(left, right + 1); } break; } } }
return result; } }
|
2.2 双指针-哈希集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| import java.util.HashMap; import java.util.Map;
class Solution { Map<Character, Integer> tMap = new HashMap<>(); Map<Character, Integer> sMap = new HashMap<>();
public String minWindow(String s, String t) { for (int i = 0; i < t.length(); i++) { Character c = t.charAt(i); tMap.put(c, tMap.getOrDefault(c, 0) + 1); }
int minLen = Integer.MAX_VALUE; String result = "";
int left, right; for (left = 0, right = 0; right < s.length(); right++) { Character c = s.charAt(right); sMap.put(c, sMap.getOrDefault(c, 0) + 1);
while (check()) { int length = right - left + 1; if (length < minLen) { minLen = length; result = s.substring(left, right + 1); } c = s.charAt(left); sMap.put(c, sMap.get(c) - 1); left++; } }
return result; }
public boolean check() { for (Map.Entry<Character, Integer> entry : tMap.entrySet()) { Character c = entry.getKey(); if (sMap.getOrDefault(c, 0) < entry.getValue()) { return false; } } return true; } }
|
2.3 双指针-哈希数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| class Solution { public String minWindow(String s, String t) { int[] tArr = new int[128]; int[] sArr = new int[128];
for (int i = 0; i < t.length(); i++) { tArr[t.charAt(i)]++; }
int left = 0, right = 0; int minLen = Integer.MAX_VALUE; String result = "";
while (right < s.length()) { sArr[s.charAt(right)]++; while (check(sArr, tArr)) { int length = right - left + 1; if (length < minLen) { minLen = length; result = s.substring(left, right + 1); } sArr[s.charAt(left)]--; left++; } right++; } return result; }
public boolean check(int[] sArr, int[] tArr) { for (int i = 'A'; i <= 'Z'; i++) { if (sArr[i] < tArr[i]) { return false; } } for (int i = 'a'; i <= 'z'; i++) { if (sArr[i] < tArr[i]) { return false; } } return true; } }
|
2.4 优化
- 上面两种方法,每次都需要调用
check()
函数,我们能不能优化一下呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| class Solution { public String minWindow(String s, String t) { int[] tArr = new int[128]; int[] sArr = new int[128];
int cnt = 0; for (int i = 0; i < t.length(); i++) { if (tArr[t.charAt(i)]++ == 0) { cnt++; } }
int left = 0, right = 0; int minLen = Integer.MAX_VALUE; String result = "";
while (right < s.length()) { char c = s.charAt(right); sArr[c]++; if (sArr[c] == tArr[c]) { cnt--; } while (cnt == 0) { int length = right - left + 1; if (length < minLen) { minLen = length; result = s.substring(left, right + 1); } char x = s.charAt(left); sArr[x]--; if (sArr[x] < tArr[x]) { cnt++; } left++; } right++; } return result; } }
|