LeetCode刷题: 【76】最小覆盖子串

1. 题目

给你一个字符串 S、一个字符串 T,请在字符串 S里面找出:包含 T所有字母的最小子串。

示例:

输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"
说明:

如果 S 中不存这样的子串,则返回空字符串 ""
如果 S 中存在这样的子串,我们保证它是唯一的答案。

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

2. 思路

(1) 对于这类求子串问题,首先想到的方法就是滑动窗口,即通过双指针先后遍历字符串,在遍历过程中对当前窗口进行判断,当满足条件时更新记录,直至遍历完成。

(2) 那么如何在搜索过程中确定当前窗口是否满足条件呢?众所周知,hash表的查找速度是最快的,我们尝试使用hash表将目标字符和需求出现次数做一个映射,例如T=“AAABBC”,那么建立映射
MAP:{A:3, B:2, C:1},在头指针前进过程中,若头指针所指向元素存在于MAP中,则使对应元素映射值-1,当所有映射值小于零的时候(在满足条件之前可能有的映射值会变为负数,为缩短条件判断时间,采用一个变量记录归零值的数量,当该变量等于MAP的长度时,条件被满足),认为当前窗口符合条件,开始窗口收缩(尾指针开始追赶),尾指针指向元素存在于MAP中时,使映射值+1,当任意映射值大于零时,就获得了一个最小窗口。继续移动头指针,开始下一轮的搜索。

3. 代码

class Solution {
	public String minWindow(String s, String t) {

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

		// 建立映射计数
		Map<Character, Integer> temp = new HashMap<>();

		for (char c : t.toCharArray()) {
			temp.put(c, temp.getOrDefault(c, 0) + 1);
		}

		// 字符串转换为数组
		char[] ss = s.toCharArray();

		// 建立双指针
		int head = 0, tail = 0;

		// 记录最小宽度 以及当时尾指针位置
		int min = s.length();
		int mt = -1;

		// 开始搜索
		for (int size = temp.size(); head < s.length(); head++) {
			// 计数搜寻(头指针向后移动)
			if (temp.keySet().contains(ss[head])) {
				Integer num = temp.get(ss[head]);
				temp.put(ss[head], num - 1);
				if (num == 1)
					size--;
			}

			// 缩小范围(当窗口满足条件,尾指针开始追赶;不满足条件时停止追赶)
			if (size == 0) {
				do {
					if (temp.keySet().contains(ss[tail])) {
						Integer num = temp.get(ss[tail]) + 1;
						temp.put(ss[tail], num);
						if (num == 1)
							size++;
					}
					tail++;
				} while (size == 0);

				// 记录最小值
				if (head - tail + 1 < min) {
					mt = tail - 1;
					min = head - mt;
				}
			}
		}

		return mt == -1 ? "" : s.substring(mt, mt + min + 1);
	}
}

4. 复杂度

mT 的长度,nS 的长度

时间复杂度:O(m+n)
空间复杂度:O(n)

你可能感兴趣的:(文章,算法,LeetCode)