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