算法—leetcode—76—最小覆盖子串

282 阅读1分钟

题目

  1. 最小覆盖子串

题目描述

给你一个字符串s、一个字符串t;返回s中涵盖t所有字符的最小子串。

说明

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

题目概述

就是说要在s中找到包含t中全部字母的一个子串,且这个子串一定是所有可能子串中最短的。

案例

示例一

输入:s = "ADOBECODEBANC", t = "ABC"
输出:"BANC"

示例二

输入:s = "a", t = "a"
输出:"a"

思路

1、我们在字符串S中使用双指针中的左右指针技巧,初始化left = right = 0,把索引左闭右开区间 [left, right) 称为一个「窗口」。
2、我们先不断地增加right指针扩大窗口[left, right),直到窗口中的字符串符合要求(包含了T中的所有字符)。
3、此时,我们停止增加right,转而不断增加left指针缩小窗口[left, right),直到窗口中的字符串不再符合要求(不包含T中的所有字符了);同时,每次增加left,我们都要更新一轮结果。
4、重复第2和第3步,直到right到达字符串S的尽头。

代码

package leetcode

import "log"

// minWindow
// 最小覆盖子串
func minWindow(s string, t string) string {
	// 初始化计数器,分别记录【窗口】中字符的出现次数和T中字符串出现次数
	window := map[byte]int{}
	need := map[byte]int{}
	for i := 0; i < len(t); i++ {
		cur := t[i]
		need[cur]++
	}

	maxNum := 1<<31 - 1

	// 初始化窗口的两端,区间[left, right)
	left, right := 0, 0

	// 表示窗口中满足need条件的字符个数
	// 如果valid和len(need)的大小相同,则说明窗口已满足条件,已经完全覆盖了字符串t
	valid := 0

	// @TODO 记录最小覆盖子串的起始索引及长度,计算最终结果使用
	start, length := 0, maxNum
	log.Println(right, left, start, length)
    
    // 结束条件:right到达s的尽头
	for right < len(s) {
        // 即将移入窗口的值
		cur := s[right]
        right++
        
		// 进行窗口数据更新
        // 首先校验是否在T串
		if _, ok := need[cur]; ok {
			window[cur]++

			// 表示窗口中满足need条件的字符个数
			if window[cur] == need[cur] {
				valid++
			}
		}

		// 判断窗口是否需要收缩:valid==len(need)代表当前窗口已经满足need
		for valid == len(need) {
			// 在这里更新最小覆盖子串:最终结果产生位置
			if (right - left) < length {
				start = left
				length = right - left
			}

			// 将移出窗口的字符
			delS := s[left]
			left++
			if _, ok := need[delS]; ok {
               // 既要移除窗口的值,也要移除验证的值
				if window[delS] == need[delS] {
					valid--
				}
				window[delS]--
			}
		}
	}
	// 返回最小覆盖子串
	if length == maxNum {
		return ""
	} else {
        log.Println(start, length)
		return s[start:start+length]
	}
}

参考

来源:力扣(LeetCode)
链接:leetcode-cn.com/problems/mi…
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

labuladong的算法小抄