76. Minimum Window Substring

时间:2022-05-05 07:12:17

题目:

Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).

For example,
S = "ADOBECODEBANC"
T = "ABC"

Minimum window is "BANC".

Note:
If there is no such window in S that covers all characters in T, return the empty string "".

If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in S.

链接:  http://leetcode.com/problems/minimum-window-substring/

题解:

也是一道卡了很久的题。最近工作也忙,面试也忙,装修房子也忙,都没时间好好刷题。每天下班就跑建材市场,买材料干活。现在小腿肚子上的肌肉一走路就疼-_____-!! 早上电面了GS,答得乱七八糟...不找借口,该刷题还是得刷。

还是采用sliding window。先把t的字符放入一个hashmap中,然后对s进行遍历。  遍历过程中先确定window的右边界,找到满足条件的右边界以后,返回去找左边界,对多余元素以及不在t中的元素,我们都要做相应处理。 完毕之后将window左边界向右移动一个距离,进行下次查找。时间复杂度是O(n)因为每个元素只被访问两次。 代码依然写得很罗嗦,二刷的时候一定要简练。好像也可以用Deque或者double linkedlist来做,要研究一下。

睡觉去了!

Time Complexity - O(n), Space Complexity - O(n)

public class Solution {
public String minWindow(String s, String t) {
if(s == null || t == null || s.length() == 0 || t.length() == 0)
return "";
HashMap<Character, Integer> tMap = new HashMap<>(); for(int i = 0; i < t.length(); i++) { //put t into tMap
if(tMap.containsKey(t.charAt(i)))
tMap.put(t.charAt(i), tMap.get(t.charAt(i)) + 1);
else
tMap.put(t.charAt(i), 1);
} int lo = 0, min = s.length() + 1, count = 0, minLo = -1; boolean hasWindowChar = false; HashMap<Character, Integer> sMap = new HashMap<>(); for(int i = 0; i < s.length(); i++) {
if(!hasWindowChar && !tMap.containsKey(s.charAt(i))) {
lo++;
continue;
} hasWindowChar = true; //found start index
if(!tMap.containsKey(s.charAt(i))) //skip char not in t
continue;
if(sMap.containsKey(s.charAt(i))) //put current char into sMap
sMap.put(s.charAt(i), sMap.get(s.charAt(i)) + 1);
else
sMap.put(s.charAt(i), 1); if(sMap.get(s.charAt(i)) <= tMap.get(s.charAt(i))) //if current char <= char count in tMap, add count
count++; if(count == t.length()) { //if count == t.length(), found one solution
while(lo <= i) { //find left limit of current min sliding window
if(sMap.containsKey(s.charAt(lo))) {
if(sMap.get(s.charAt(lo)) > tMap.get(s.charAt(lo))) { //more char in sMap than which in tMap
sMap.put(s.charAt(lo), sMap.get(s.charAt(lo)) - 1); //reduce char num in sMap
lo++; //increase left limit
} else //if equal count in sMap and in tMap, it's local min
break;
} else //char not in t, skip
lo++;
} if(i - lo + 1 < min) { //if local min < global min
min = i - lo + 1; //update global min (length)
minLo = lo; //update starting index of global min
} sMap.put(s.charAt(lo), sMap.get(s.charAt(lo)) - 1); //move left bound of sliding window
lo++;
count--;
} } return minLo >= 0 ? s.substring(minLo, minLo + min) : "";
}
}

二刷:

二刷又遇到这一题的时候有一种似曾相识的感觉,就象一个老朋友,静静等待你的来访...  -____-。也有用Bitmap来做的,速度可能更快一些。

下面我们来分析一下思路。

读完题目以后发现题目给定String s和t,要求s中包含t所有字符的的最短字符串。比如  s = abcc, t = abc,那么最短就是前面的abc。或者 s = cccab, t = abc,那么最短就是cab。

  1. 这里我们主要考虑先使用一个HashMap<Character, Integer> tMap来统计t里的字符和出现频率,然后使用滑动窗口的方法来解决这个问题。
  2. 我们需要另外一个HashMap来负责统计在遍历string s的时候,当前的字符和频率 = curMap
  3. 以及几个integer变量
    1. lo 表示当前窗口的最左边界
    2. count表示当前窗口里,也在t中出现过的字符数,注意这里不包含多余的字符,比如 s = caaab, t = aabc,那么中间多余的a不应该被计算入count里,因为是duplicate,但最短值仍然是caaab。
    3. minLo ,明楼表示到现在为止最短字符串的左边界, 初始化为 -1
    4. min表示到现在为止最短字符串的长度, 初始化为 s.length() + 1
    5. 最后返回结果的时候就判断minLo是否 >= 0,假如是,说明存在结果,那么我们只要返回s.substring(minLo, minLo + min)就可以了, 否则不存在,返回""
  4. 接下来我们从0开始遍历整个s字符串,在遍历的过程中有以下几种情况:
    1. 假设当前位置i字符为c, 假如 tMap中不包含这个字符。
      1. 假如curMap.size() == 0,这时候窗口的左边界肯定在位置i 左边,所以我们进行 lo++。然后我们continue,跳过位置i。
      2. 假如curMap.size() > 0,这时候因为我们已经有了左边界,这个位置虽然元素不在tMap里,但也要占一个count的坑,我们不改变lo, 直接continue,跳过位置i。
    2. 否则这个字符c在tMap里,我们可以把这个字符加入到当前的curMap里。 假如在这个加入操作以后,curMap.get(c) <= tMap.get(c),这时候可以增加count++,我们认为这次找到了一个t中的字符,而且不是重复。
    3. 当count == tLen, 这时候的window里包含了t中所有的字符,但有可能还有多余字符,那么我们现在固定右边界,回去查找精确的左边界
      1. 我们从lo到i开始遍历这个window, 假设s.charAt(lo) = loChar
      2. 假如loChar不在当前map里,说明它和count无关,我们可以lo++
      3. 否则,loChar在当前map里,这时候我们要进行判断
        1. 假如curMap.get(loChar) > tMap.get(loChar), 说明有duplicate,我们减少curMap中的loChar树木,并且lo++
        2. 否则curMap的loChar数目和tMap中正好相等,这时候我们找到了合理的左边界,break跳出window的遍历
      4. 此时我们要对这个window的长度 i - lo + 1与之前保存的最小window长度min进行比较,假如这个window更短,我们更新min = i - lo + 1,并且更新minLo = lo
      5. 完后,我们要把window的左边界元素减少一个,并且count--,然后lo++,就是把窗口的左边界向右移动一个单位,来继续寻找接下来可能符合条件的窗口
  5. 最后根据minLo的位置我们返回结果。

Java:

Time Complexity - O(n), Space Complexity - O(n)

public class Solution {
public String minWindow(String s, String t) {
if (s == null || t == null || s.length() < t.length()) {
return "";
}
int sLen = s.length(), tLen = t.length();
Map<Character, Integer> tMap = new HashMap<>();
for(int i = 0; i < tLen; i++) { //put t into tMap
if(tMap.containsKey(t.charAt(i)))
tMap.put(t.charAt(i), tMap.get(t.charAt(i)) + 1);
else
tMap.put(t.charAt(i), 1);
}
Map<Character, Integer> curMap = new HashMap<>();
int lo = 0, minLo = -1, min = s.length() + 1, count = 0; for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (!tMap.containsKey(c)) {
if (curMap.size() == 0) {
lo++;
}
continue;
}
if (curMap.containsKey(c)) {
curMap.put(c, curMap.get(c) + 1);
} else {
curMap.put(c, 1);
}
if (curMap.get(c) <= tMap.get(c)) {
count++;
}
if (count == tLen) {
for (int j = lo; j < i; j++) {
char loChar = s.charAt(j);
if (!curMap.containsKey(loChar)) {
lo++;
} else if (curMap.get(loChar) > tMap.get(loChar)) {
curMap.put(loChar, curMap.get(loChar) - 1);
lo++;
} else {
break;
}
}
if (i - lo + 1 < min) {
min = i - lo + 1;
minLo = lo;
}
curMap.put(s.charAt(lo), curMap.get(s.charAt(lo)) - 1);
lo++;
count--;
}
}
return minLo >= 0 ? s.substring(minLo, minLo + min) : "";
}
}

2/5/2016:

中午组织同事们在麒麟金阁吃了早茶,一共来了十一位,大家都很高兴。我也提前给大家拜个早年,希望大家身体健康,事业进步,幸福美满。

三刷:

写起来确实费劲 -____-!! 速度很慢,并不是很好的方法,要改进。

Java:

public class Solution {
public String minWindow(String s, String t) {
if (s == null || t == null || s.length() < t.length()) return "";
Map<Character, Integer> tMap = new HashMap<>();
int sLen = s.length(), tLen = t.length();
for (int i = 0; i < tLen; i++) {
char c = t.charAt(i);
if (!tMap.containsKey(c)) tMap.put(c, 1);
else tMap.put(c, tMap.get(c) + 1);
}
Map<Character, Integer> sMap = new HashMap<>();
int count = 0, lo = 0, minLo = -1, len = s.length() + 1; for (int i = 0; i < sLen; i++) {
char sc = s.charAt(i);
if (!tMap.containsKey(sc)) continue; if (!sMap.containsKey(sc)) sMap.put(sc, 1);
else sMap.put(sc, sMap.get(sc) + 1); if (sMap.get(sc) <= tMap.get(sc)) count++; if (count == tLen) {
while (!tMap.containsKey(s.charAt(lo)) || sMap.get(s.charAt(lo)) > tMap.get(s.charAt(lo))) {
if (tMap.containsKey(s.charAt(lo)) && sMap.get(s.charAt(lo)) > tMap.get(s.charAt(lo))) {
sMap.put(s.charAt(lo), sMap.get(s.charAt(lo)) - 1);
}
lo++;
}
if (i - lo + 1 < len) {
minLo = lo;
len = i - lo + 1;
}
sMap.put(s.charAt(lo), sMap.get(s.charAt(lo)) - 1);
lo++;
count--;
}
}
return minLo >= 0 ? s.substring(minLo, minLo + len) : "";
}
}

Reference:

https://leetcode.com/discuss/72701/here-10-line-template-that-can-solve-most-substring-problems

https://leetcode.com/discuss/10337/accepted-o-n-solution

https://leetcode.com/discuss/5469/is-the-length-of-t-considered-constant-or-m

https://leetcode.com/discuss/32851/share-my-neat-java-solution

https://leetcode.com/discuss/79792/the-fast-7ms-o-n-java-solution-use-only-one-array-without-map