滑动窗口算法 - LC76 最小覆盖子串

本文介绍了一道关于字符串的滑动窗口进阶题——寻找一个字符串中涵盖给定字符串t所有字符的最小子串。作者详细解析了解题步骤,包括使用map记录字符出现次数,滑动窗口的收缩策略等。
摘要由CSDN通过智能技术生成

接上文滑窗基础题:滑动窗口算法 - LC3 无重复字符的最长子串-CSDN博客,介绍了滑窗的基础题目和滑窗解法模板,这次带来滑窗进阶题解。

76. 最小覆盖子串

困难

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。

这道进阶题其实不算难,但细节拉满,漏掉一处都无法通过,我将解题的关键点都注释在代码中。


func minWindow(s string, t string) string {
	need := make(map[byte]int)
	window := make(map[byte]int)

	for i := 0; i < len(t); i++ {
		need[t[i]]++
	}

	valid := 0                     //用来记录window中寻找到到满足覆盖t中的字符的个数
	start, end := 0, math.MaxInt32 //初始化找到的覆盖子串的左右指针
	l, r := 0, 0                   // 初始化左右指针
	for r < len(s) {
		//滑窗模板 先动右指针
		c := s[r]
		r++
		if need[c] > 0 { //这个if是为了只处理t中出现的字符串,减少运算,也可省去
			window[c]++
			if window[c] == need[c] {
				valid++ //如果c字符在两个map里出现次数相同,那么说明合法的覆盖字符又多个一个
			}

		}

		//达到滑窗收缩的条件,valid为算法记录的合法覆盖字符数量,len(need)为所有需要覆盖的字符数量
		for valid == len(need) {
			//收缩前,先取当前覆盖子串的长度,更新最小长度
			if r-l < end-start {
				start = l
				end = r
			}
			//滑窗模板 移动左指针
			d := s[l]
			l++

			if need[d] > 0 { //这个if是为了只处理t中出现的字符串,减少运算,也可省去
				if window[d] == need[d] {
					valid-- //左指针即将向后移动,字符d的合法覆盖将不再满足,valid--
				}
				window[d]--
			}
		}
	}
	if end == math.MaxInt32 {
		return ""
	}
	return s[start:end]
}

这里有个点可能会有人有疑问,因为字符串切片是包头不包尾的,即end所在的index是不会被截取的,为什么覆盖子串字符串的是s[start:end]而不是s[start:end+1]?

这是因为我们在外层更新右指针r的for循环里,每次进入循环前先直接更新了右指针r++,也就是说r已经被提前推到下一个索引位置了。假设把r++放到整个for循环的最后,那么end=r就可以改成end=r+1,like this

for r < len(s) {
		//滑窗模板 先动右指针
		c := s[r]

		if need[c] > 0 { //这个if是为了只处理t中出现的字符串,减少运算,也可省去
			window[c]++
			if window[c] == need[c] {
				valid++ //如果c字符在两个map里出现次数相同,那么说明合法的覆盖字符又多个一个
			}

		}

		//达到滑窗收缩的条件,valid为算法记录的合法覆盖字符数量,len(need)为所有需要覆盖的字符数量
		for valid == len(need) {
			//收缩前,先取当前覆盖子串的长度,更新最小长度
			if r-l < end-start {
				start = l
				end = r + 1
			}
			//滑窗模板 移动左指针
			d := s[l]
			l++

			if need[d] > 0 { //这个if是为了只处理t中出现的字符串,减少运算,也可省去
				if window[d] == need[d] {
					valid-- //左指针即将向后移动,字符d的合法覆盖将不再满足,valid--
				}
				window[d]--
			}
		}
		r++
	}

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值