LeetCode每日一题 最小覆盖子串 双指针,移动窗口

题目

给你一个字符串 S、一个字符串 T 。请你设计一种算法,可以在 O(n) 的时间复杂度内,从字符串 S 里面找出:包含 T 所有字符的最小子串。
在这里插入图片描述

分析

遇到子串搜索类型的问题,我们第一想到的一般都是滑动窗口的方法,然后再设定窗口滑动是否要变动,如何变动问问题。

我们可以先分析利用双指针来操作滑动窗口的问题,然后在依次解决算法转化的具体问题。

首先,双指针的关键之处在于左右指针,右指针先行移动,先找到符合条件的最大子串,即最大覆盖子串,然后左指针再移动,找到在该情景下的最小覆盖子串,即此子串为该情况下的最小子串。

然后,右指针继续移动,找寻新的窗口,以求得整个 s 子串中,最小覆盖 t 的子串,具体操作思路如下图:

初始时,左右指针在起始位置,准备移动建造窗口

假设 t 子串为 abc
在这里插入图片描述
然后右指针移动,建造窗口,找到最大覆盖子串

在这里插入图片描述
然后移动左指针,先找到最小子串,记录然后继续移动

在这里插入图片描述
在这里插入图片描述
然后右指针继续搜索,左指针继续移动,找到新的子串和之前的对比,直到找到最小覆盖子串
在这里插入图片描述
因为我们要将三个不同的字符依次搜索成功,如果用 || 或 && 的逻辑语句来连接,很有可能出现最后存在有三个或者多个相同字符,所以建议可以直接直接使用 map 搜索,快速简答还可以避免重复。

关于算法的具体操作,我们需要创建一个窗口,一个容纳 t 字符串,左右指针,窗口长度和窗口起始指针,还有一个参数记录已被搜索的字样,前提摆好,代码如下,时间复杂度为 O(n2):

func minWindow(s string, t string) string {
	//先行创建窗口和搜索框
	need, window := map[byte]int{}, map[byte]int{}
	for i := range t {
		need[t[i]]++
	}
	//检索是否该字符被搜索过,记录长度和起始位置
	valid := 0
	index, length := 0, math.MaxInt32
	//设定左右指针
	left, right := 0, 0
	//设定循环长度,当右指针搜索到最后一字符时停止
	for right < len(s) {
		tempAdd := s[right]
		right++
		//利用map判断是否符合,存在有符合字段,窗口移动创建
		if _, ok := need[tempAdd]; ok {
			window[tempAdd]++
			//判断搜索到几位
			if window[tempAdd] == need[tempAdd] {
				valid++
			}
		}
		//设定搜索范围
		for valid == len(need) {
			if right-left < length {
				index = left
				length = right - left
			}
			tempDel := s[left]
			left++
			//同样的,判断是否符合条件
			if _, ok := need[tempDel]; ok {
				window[tempDel]--
				//若不符合覆盖条件,则退出
				if window[tempDel] < need[tempDel] {
					valid--
				}
			}
		}
	}
	//若无符合条件,则返回空
	if length == math.MaxInt32 {
		return ""
	}
	//若有,返回最大覆盖子串
	return s[index : index+length]
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值