Leetcode 76:最小覆盖子串

问题

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”

分析

是一个看上去感觉可解的问题。(terrible solution)
简单解释一下这道题的思路:要在s找包括t中所有字符的最短子串,且时间复杂度要求是O(n)。
遍历字符串s,并记录当前子串的开始位置和结束位置;如果在该范围内,包括t中所有字符,则记录该子串,更新最短子串。
(好像没啥好说的)

主要包括几个步骤:
1)遍历t,记录t中所有字符的出现次数到precount中;
2)遍历s,curr记录当前遍历到的位置;记录从start——end范围内的子串是否满足包括字符串t的要求;若满足:更新start,end;更新最短子串;更新记录是否满足的标志。(这个标志在terriblesolution和brilliantsolution中实现不同,并决定了terriblesolution是terrible)
若不满足:移动curr;
terrible solution: 时间复杂度O(|S|*|T|)
是否满足的标志:用了一个count Map,记录当前子串中包含t中对应字符的个数;在判断是否满足时,遍历precount,与count比较;
即若子串“abcaad”,t:“ade”, 则当前,precount:{a=1,d=1,e=1};count:{a=1,d=1,e=0}

brilliant solution: 时间复杂度O(|S|+|T|)
是否满足的表示:
变量formed:记录s的当前子串中已满足t中字符个数要求的字符数。
变量required:记录t中不同字符的个数;
即若子串“abcaad”,t:“ade”, 则当前,required=3,formed=2;

代码

terrible soluntion:

public String minWindow2(String s, String t) {
	// very very awful
	if (s == null || t == null)
		return "";
	if (s.length() < t.length())
		return "";
	Map<Character, Integer> precount = new HashMap<Character, Integer>();
	Map<Character, Integer> count = new HashMap<Character, Integer>();
	for (int i = 0; i < t.length(); i++) {
		if (!precount.containsKey(t.charAt(i))) {
			precount.put(t.charAt(i), 1);
			count.put(t.charAt(i), 0);
		} else {
			precount.put(t.charAt(i), precount.get(t.charAt(i)) + 1);
		}
	}
	Queue<Integer> queue = new LinkedList<Integer>();
	int start = 0, end = 0;
	int len = s.length();
	int curr = 0;
	String ans = "";
	while (curr < s.length()) {
		char ch = s.charAt(curr);
		if (!precount.containsKey(ch)) {

		} else {
			queue.offer(curr);
			if (!count.containsKey(ch)) {
				count.put(ch, 0);
			} else {
				count.put(ch, count.get(ch) + 1);
			}
			// 判断是否满足t中所有字符的出现次数的条件
			while (isCertify(count, precount)) {
				start = queue.poll();
				end = curr;
				count.put(s.charAt(start), count.get(s.charAt(start)) - 1);
				if (end - start + 1 <= len) {
					ans = s.substring(start, end + 1);
					len = end - start + 1;
				}
			}

		}
		curr++;
	}
	return ans;
}

private boolean isCertify(Map<Character, Integer> count, Map<Character, Integer> precount) {
	// TODO Auto-generated method stub
	Set<Character> set = precount.keySet();
	for (char a : set) {
		if (count.get(a) == null)
			return false;
		if (precount.get(a) > count.get(a))
			return false;
	}
	return true;
}

brilliant solution:(来自Leetcode官方解答)

public String minWindow(String s, String t) {

    if (s.length() == 0 || t.length() == 0) {
        return "";
    }

    // Dictionary which keeps a count of all the unique characters in t.
    Map<Character, Integer> dictT = new HashMap<Character, Integer>();
    for (int i = 0; i < t.length(); i++) {
        int count = dictT.getOrDefault(t.charAt(i), 0);
        dictT.put(t.charAt(i), count + 1);
    }

    // Number of unique characters in t, which need to be present in the desired window.
    int required = dictT.size();

    // Left and Right pointer
    int l = 0, r = 0;

    // formed is used to keep track of how many unique characters in t
    // are present in the current window in its desired frequency.
    // e.g. if t is "AABC" then the window must have two A's, one B and one C.
    // Thus formed would be = 3 when all these conditions are met.
    int formed = 0;

    // Dictionary which keeps a count of all the unique characters in the current window.
    Map<Character, Integer> windowCounts = new HashMap<Character, Integer>();

    // ans list of the form (window length, left, right)
    int[] ans = {-1, 0, 0};

    while (r < s.length()) {
        // Add one character from the right to the window
        char c = s.charAt(r);
        int count = windowCounts.getOrDefault(c, 0);
        windowCounts.put(c, count + 1);

        // If the frequency of the current character added equals to the
        // desired count in t then increment the formed count by 1.
        if (dictT.containsKey(c) && windowCounts.get(c).intValue() == dictT.get(c).intValue()) {
            formed++;
        }

        // Try and contract the window till the point where it ceases to be 'desirable'.
        while (l <= r && formed == required) {
            c = s.charAt(l);
            // Save the smallest window until now.
            if (ans[0] == -1 || r - l + 1 < ans[0]) {
                ans[0] = r - l + 1;
                ans[1] = l;
                ans[2] = r;
            }

            // The character at the position pointed by the
            // `Left` pointer is no longer a part of the window.
            windowCounts.put(c, windowCounts.get(c) - 1);
            if (dictT.containsKey(c) && windowCounts.get(c).intValue() < dictT.get(c).intValue()) {
                formed--;
            }

            // Move the left pointer ahead, this would help to look for a new window.
            l++;
        }

        // Keep expanding the window once we are done contracting.
        r++;   
    }

    return ans[0] == -1 ? "" : s.substring(ans[1], ans[2] + 1);
}

更好的方式

(来自Leetcode官方解答)预先处理字符串s,记录s中出现t中字符的位置,为filtered_S;之后的遍历s的过程,变为遍历filtered_S。
(没细看)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值