76. 最小覆盖子串

Title

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

示例:

输入: S = “ADOBECODEBANC”, T = “ABC”
输出: “BANC”

说明:

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

Analysis

滑动窗口

要求我们返回字符串S中包含字符串T的全部字符的最小窗口。

在滑动窗口类型的问题中都会有两个指针,一个用于延伸现有窗口的right指针,一个用于收缩窗口的left指针。

在任意时刻,只有一个指针运动,而另一个指针保持静止。

我们在S上滑动窗口,通过移动right指针不断扩张窗口,当包含T全部所需字符后,如果能收缩,就收缩窗口直到得到最小窗口。
在这里插入图片描述
如何判断当前的窗口包含所有T所需的字符?

我们可以用一个哈希表表示T中所有的字符以及它们的个数,用一个哈希表动态维护窗口中所有的字符以及它们的个数,如果这个动态表中包含T的哈希表中的所有字符,并且对应的个数都不小于T的哈希表中各个字符的个数,那么当前窗口就是一个可行窗口。

注意:这里 tt 中可能出现重复的字符,所以我们要记录字符的个数。

复杂度分析:

  • 时间复杂度:最坏情况下左右指针对S的每个元素各遍历一遍,哈希表中对S中的每个元素各插入、删除一次,对T中的元素各插入一次。每次检查是够可行会遍历整个T的哈希表,哈希表的大小与字符集的大小有关,设字符集的大小为C,则渐进时间复杂度为O(C⋅∣s∣+∣t∣)。
  • 空间复杂度:这里用了两张哈希表作为辅助空间,每张哈希表最多不存放超过字符集大小的键值对,设字符集的大小为C,则渐进空间复杂度为O©。

Code

def minWindow(self, s: str, t: str) -> str:
	import collections
	n, left, ans, cnt = 0, 0, '', collections.Counter(t)
	for right, ch in enumerate(s):
		if ch not in cnt:
			continue
		# 统计当前字符,判断是否已经达到数量要求
		cnt[ch] -= 1
		if cnt[ch] == 0:
			n += 1
		# 如果当前在left的字符没有必要,可以右移左指针
		while s[left] not in cnt or cnt[s[left]] < 0:
			if s[left] in cnt:
				cnt[s[left]] += 1
			left += 1
		if n == len(cnt):
			if not ans or len(ans) > right - left + 1:
				ans = s[left: right + 1]
	return ans

如何优化?

如果S = XX⋯XABCXXXX,T = ABC,那么显然[XX⋯XABC]是第一个得到的可行区间,得到这个可行区间后,我们按照收缩窗口的原则更新左边界,得到最小区间。

在这个过程中其实做了很多无用功,更新右边界的时候延伸进了很多无用的X,更新左边界的时候又收缩了这些无用的X,而这些无用的操作只是为了得到短短的ABC。

其实在S中,只需要关心T中出现的字符,可以先对S进行预处理,扔掉那些T中没有出现的字符,然后再做滑动窗口。

例如,对于XXABXXC的情况,在统计长度的时候可以扔掉前两个X,但是不扔掉中间的X。

展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 代码科技 设计师: Amelia_0503
应支付0元
点击重新获取
扫码支付

支付成功即可阅读