[LeetCode] 76. Minimum Window Substring(最小窗口子串)

Description

Given two strings s and t, return the minimum window in s which will contain all the characters in t. If there is no such window in s that covers all characters in t, return the empty string "".
给定两字符串 st,返回 s 的一个最小窗口(子串),该子串包含 t 中所有字符。如果找不到这样的窗口(子串),则返回空串 ""

Note that If there is such a window, it is guaranteed that there will always be only one unique minimum window in s.
注意,如果存在这样的窗口(子串),输入保证该窗口(子串)唯一。

Examples

Example 1

Input: s = "ADOBECODEBANC", t = "ABC"
Output: "BANC"

Example 2

Input: s = "a", t = "a"
Output: "a"

Constraints

  • 1 <= s.length, t.length <= 1e5
  • s and t consist of English letters.

Follow up

Could you find an algorithm that runs in O(n) time?

Hints

  1. Use two pointers to create a window of letters in S, which would have all the characters from T.

  2. Since you have to find the minimum window in S which has all the characters from T, you need to expand and contract the window using the two pointers and keep checking the window for all the characters. This approach is also called Sliding Window Approach.

    L ------------------------ R , Suppose this is the window that contains all characters of T
    
            L----------------- R , this is the contracted window. We found a smaller window that still contains all the characters in **T**
    

    When the window is no longer valid, start expanding again using the right pointer.

Solution

子串问题优先考虑滑动窗口。首先需要统计 t 中字母出现的次数。然后扩展窗口右边界,寻找能包含 t 中所有字母的窗口,再扩展窗口左边界,去掉“不必要的字母”。代码如下:

class Solution {
    fun minWindow(s: String, t: String): String {
        if (s.length < t.length) {
            return ""
        }
        val countMap = hashMapOf<Char, Int>()
        t.forEach { countMap[it] = (countMap[it] ?: 0) + 1 }

        var left = 0
        // 统计窗口内匹配到了 t 中的多少字母
        var match = 0
        var minLength = Int.MAX_VALUE
        var result = ""

        for (right in s.indices) {
            // 为什么直接在原 map 上改?
            // 这是为了方便统计,直接在 map 上改,那么判断“窗口恰好包含 t 中所有字母”的条件是
            // map 里所有 t 包含的字母,对应的频率为 0
            // 对于 t 不包含的字母,其对应的 value 永远小于 0
            countMap[s[right]] = (countMap[s[right]] ?: 0) - 1
            if (countMap.getValue(s[right]) >= 0) {
                match++
            }
            while (match == t.length) {
                if (minLength > right - left + 1) {
                    minLength = right - left + 1
                    result = s.substring(left..right)
                }
                countMap[s[left]] = (countMap[s[left]] ?: 0) + 1
                if (countMap.getValue(s[left]) > 0) {
                    // 窗口收缩导致 t 中有一个字母没包括进去
                    match--
                }
                left++
            }
        }
        return result
    }
}
原文地址:https://www.cnblogs.com/zhongju/p/14182901.html