最小窗口子串。题意是给两个字符串S和T,T比较短。请输出一个最小的S的子串,包含了T中出现的所有字母。例子,
Example:Input: S = "ADOBECODEBANC", T = "ABC"Output: "BANC"
这个题因为有时间复杂度的要求所以只能用到一个叫做滑动窗口的思想。在S上放两个指针,left和right,中间包含的就是含有T中所有字母的子串。除了S中找不到T中包含的所有字母这种情况之外(返回空字符串),left和right的间距极端情况应该是S的长度。但是怎样缩小left和right之间的距离呢,会用到一个hashmap,去记录T中所有的字符是否在left和right之间是否都出现过。
[depreciated solution,因为这个思路不具有普适性。新版本的题解可以直接移步这里] 首先用hashmap遍历T,把T里面所有的字母都放进hashmap,同时记录hashmap中所有的key的个数,记为count,然后遍历S。遍历S的时候,一开始一定不满足条件,所以一开始右指针往右移动,同时去看当前扫描到的char是否存在于hashmap,如果存在则减去这个字母在hashmap的value(map[s[right]}--);当某个字母在hashmap里的value为0时,count--,说明这个key都没了。
当count为0的时候说明所有需要找的字母都找到了,此时可以看一下当前left和right的间距与res谁短,并同时考虑缩短left和right之间的距离了。因为right所在的位置一定是满足条件的所以要缩短距离只能试试看left往左移动。看left当前位置的char是否在hashmap里面,从而说明这个字母是否在T中存在,若在则对其value++(map[s[left]]++)。如果当前字母的value被++完了之后大于0,count也要++,说明在left和right中间依然还有这个字母(map[s[left]])存在。
时间O(n)
空间O(n) - hashmap
JavaScript实现
1 /** 2 * @param {string} s 3 * @param {string} t 4 * @return {string} 5 */ 6 var minWindow = function (s, t) { 7 let ans = ''; 8 // save all the letters in t to a hashmap 9 let map = {}; 10 t.split('').forEach(ch => map[ch] = (map[ch] || 0) + 1); 11 let count = Object.keys(map).length; 12 13 // traverse s 14 let l = 0; 15 let r = -1; 16 while (r < s.length) { 17 if (count === 0) { 18 // l~r contains t 19 if (!ans || r - l + 1 < ans.length) { 20 ans = s.slice(l, r + 1); 21 } 22 // get rid of curr ch and then move l 23 if (map[s[l]] !== undefined) { 24 map[s[l]]++; 25 } 26 if (map[s[l]] > 0) { 27 count++; 28 } 29 l++; 30 } else { 31 // l~r doesn't contain t 32 // move r and add new ch 33 r++; 34 if (map[s[r]] !== undefined) { 35 map[s[r]]--; 36 } 37 if (map[s[r]] === 0) { 38 count--; 39 } 40 } 41 } 42 return ans; 43 };
Java实现
1 class Solution { 2 public String minWindow(String s, String t) { 3 int[] map = new int[128]; 4 for (char c : t.toCharArray()) { 5 map[c]++; 6 } 7 // end指针在右边,start指针在左边 8 int start = 0; 9 int end = 0; 10 // 最短子串的起点 11 int minStart = 0; 12 // 最短子串的长度 13 int minLen = Integer.MAX_VALUE; 14 int counter = t.length(); 15 while (end < s.length()) { 16 char c1 = s.charAt(end); 17 if (map[c1] > 0) { 18 counter--; 19 } 20 map[c1]--; 21 end++; 22 // 如果找到了所有t中的字符,可以试着缩小窗口的距离了 23 while (counter == 0) { 24 // 试图更新最短子串的长度和起点 25 if (minLen > end - start) { 26 minLen = end - start; 27 minStart = start; 28 } 29 char c2 = s.charAt(start); 30 map[c2]++; 31 start++; 32 if (map[c2] > 0) { 33 counter++; 34 } 35 } 36 } 37 return minLen == Integer.MAX_VALUE ? "" : s.substring(minStart, minStart + minLen); 38 } 39 }