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.
题目描述:给定两个字符串S和T,求出S中的最短子串,满足条件:该子串中的部分或全部字符可以重新排列组合形成字符串T。
题目要求:算法的时间复杂度为O(n)。
思路分析:该问题可以直接brute force求解,但暴力解法的时间复杂度显然无法达到题目要求。这里采用了在字符串处理中较为常用的方法:先建立一个字典,然后维护一个窗口,在原字符串上滑动求解。
具体的做法是:
首先。遍历字符串T,建立以字符--出现次数为键值对的HashMap,时间复杂度为O(m);
然后,遍历字符串S,维护这个HashMap:(这里的时间复杂度为O(n))
1. 如果字符不在字典中,就直接跳过不处理;
2. 否则,遇到字典中字符就将对应字符的数量减一,如果当前字符是以窗口left--right中组成字符串T仍然需要的,就将count++,表示目前已经取到了count个组成字符串T所必需的字符了;
3. 接着,如果count与T的长度相等,就判断此时的窗口长度是否比之前记录的最小长度小,如果是,就将最小长度值更新,最小长度对应的起点标记为start;
4. 为了继续向后遍历,需要将left不断向右推,判断当前left是否为组成T的必需字符,如果是,将count--,使count不再等于T的长度,迫使对S的遍历继续进行,否则,只要将left向右推即可;
综上所述,空间复杂度也就是T字符串的长度O(m)。注:在分析中默认满足n>=m,否则就属于corner case,可以直接返回空串。
代码如下:
public class Solution {
public String minWindow(String s, String t) {
// corner case
int sLen = s.length();
int tLen = t.length();
if (sLen < tLen || sLen==0 || tLen==0) {
return "";
}
// initialize the hashmap for each character in target string
HashMap<Character, Integer> map = new HashMap<>();
for (int i=0; i<tLen; i++) {
if (map.containsKey(t.charAt(i))) {
map.put(t.charAt(i), map.get(t.charAt(i))+1);
} else {
map.put(t.charAt(i), 1);
}
}
int minLen = Integer.MAX_VALUE;
int left = 0;
int start = 0;
int count = 0;
// the time complexity is O(n)
for (int right=0; right<sLen; right++) {
if (map.containsKey(s.charAt(right))) {
// change the value in hashmap
map.put(s.charAt(right), map.get(s.charAt(right))-1);
if (map.get(s.charAt(right))>=0) {
// the character is valid
count++;
}
while (count==tLen) {
// the target string
if (right-left+1<minLen) {
minLen = right-left+1;
start = left;
}
if (map.containsKey(s.charAt(left))) {
map.put(s.charAt(left), map.get(s.charAt(left))+1);
if (map.get(s.charAt(left))>0) {
// the left character is used in target string
count--;
}
}
left++;
}
}
}
if (minLen==Integer.MAX_VALUE) {
return "";
}
return s.substring(start, start+minLen);
}
}