Leetcode 76. Minimum Window Substring

12 篇文章 0 订阅
3 篇文章 0 订阅

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).

Example:

Input: S = “ADOBECODEBANC”, T = “ABC”
Output: “BANC”
(Note: T中可能会有重复字母)

method 1 sliding window

摘自leetcode discuss

  1. 首先使用一个map记录字符串t中出现的字符及其频率
  2. 首先从头开始,在s中寻找包含t的一个区间,然后滑动窗口求最小,如何寻找包含t的区间,使用计数器count来实现。
  3. 如何滑动窗口求最小: 1. 检查左指针所指字符是否属于t,如果不属于,可以直接滑动窗口,缩小窗口; 2.如果属于t,仍然滑动窗口,并且移动右指针,表示我们希望在右指针扩大的图中找到我们刚刚删去的字符
String minWindow(String s, String t) {
        HashMap<Character, Integer> map = new HashMap();
        for(char c : t.toCharArray()){
            if(map.containsKey(c)){
                map.put(c, map.get(c)+1);
            }
            else{
                map.put(c, 1);
            }
        }
        int left = 0, minLeft=0, minLen =s.length()+1, count = 0;
        for(int right = 0; right<s.length(); right++){
            char r = s.charAt(right);
            if(map.containsKey(r)){//the goal of this part is to get the first window that contains whole t
                map.put(r, map.get(r)-1);
                if(map.get(r)>=0) count++;//identify if the first window is found by counting frequency of the characters of t showing up in S
            }
            while(count == t.length()){//if the count is equal to the length of t, then we find such window
                if(right-left+1 < minLen){//jsut update the minleft and minlen value
                    minLeft = left;
                    minLen = right-left+1;
                }
                char l = s.charAt(left);
                if(map.containsKey(l)){//starting from the leftmost index of the window, we want to check if s[left] is in t. If so, we will remove it from the window, and increase 1 time on its counter in hashmap which means we will expect the same character later by shifting right index. At the same time, we need to reduce the size of the window due to the removal.
                    map.put(l, map.get(l)+1);
                    if(map.get(l)>0) count--;
                }
                left++;//if it doesn't exist in t, it is not supposed to be in the window, left++. If it does exist in t, the reason is stated as above. left++.
            }
        }
        return minLen==s.length()+1?"":s.substring(minLeft, minLeft+minLen);
    }

method 2

使用vector的简化版本,但思想是一致的

string minWindow(string s, string t) {
        vector<int> map(128,0);
        for(auto c: t) map[c]++;
        int counter=t.size(), begin=0, end=0, d=INT_MAX, head=0;
        while(end<s.size()){
            if(map[s[end++]]-->0) counter--; //in t
            while(counter==0){ //valid
                if(end-begin<d)  d=end-(head=begin);
                if(map[s[begin++]]++==0) counter++;  //make it invalid
            }  
        }
        return d==INT_MAX? "":s.substr(head, d);
    }

https://leetcode.com/problems/minimum-window-substring/discuss/26808/Here-is-a-10-line-template-that-can-solve-most-‘substring’-problems
提供了在一个string中找到substring的模板

反面案例

自己写的代码,但是由于复杂度略高,一直卡在最后一个超长的测试用例上,为以后自己提供教训

  1. 一开始直接使用string来保存每次得到的"最小区间",但这样使用substr无疑会增加复杂度,不如使用两个int来取区间,速度更快一点
  2. 每次找新窗口都是移动左指针来寻找新窗口,不符合sliding window两根指针的用途
int findIntersection(string s, map<char, int> tMap, int start, map<char, int>& alphabet){
	int end = start;
	for (int i = start; i < s.size(); i++){
		char ch = s[i];
		if (tMap.count(ch)){
			tMap[ch]--;
			if (!alphabet.count(ch))
				alphabet[ch] = 1;
			else
				alphabet[ch]++;
			if(tMap[ch] == 0) tMap.erase(s[i]);
		}

		if (tMap.size() == 0){
			end = i;
			break;
		}
	}
	return end;
}


string minWindow(string s, string t) {
	if (t.size() == 0) return "";
	if (t.size() == 1){
		if (s.find(t) != s.npos) return t;
	}

	map<char, int> tMap;
	for (int i = 0; i < t.size(); i++){
		if (!tMap.count(t[i])) tMap[t[i]] = 1;
		else tMap[t[i]]++;
	}

	int minLength = 0;
	int minStart = 0;
	int curLength = 0;
	for (int i = 0; i <s.size(); i++){
		if (!tMap.count(s[i])) continue;

		map<char, int> alphabet;
		int high = findIntersection(s, tMap, i, alphabet);
		if (minLength != 0 && high - i + 1 >= minLength) continue;

		if (high != i){
			int low = i;
			while (low <= high){
				char ch = s[low];
				if (tMap.count(ch)){
					alphabet[ch]--;
					if (alphabet[ch] == 0){
						curLength = high - low + 1;
						if (minLength == 0){
							minLength = curLength;
							minStart = low;
							break;
						}
						else if (curLength < minLength){
							minLength = curLength;
							minStart = low;
							break;
						}
					}
				}
				else
					low++;
			}
		}

	}

	return (minLength == 0)? "":s.substr(minStart, minLength);
}

summary

  1. sliding window 思想:1. 使用两根指针,左指针缩小窗口(contract),右指针滑动寻找新的符合要求的窗口; 2. 为了满足要求,当左指针消去一个符合要求的字符时,我们会要求在右指针上获得一个符合要求的字符
  2. 使用count计数器和map联合的办法,确保我们得到符合要求的区间
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值