[Leetcode][Hard] Minimum Window Substring 最小覆盖子串| Java

2,605 阅读1分钟

问题

Minimum Window Substring Hard

Given two strings s and t of lengths m and n respectively, return the minimum window substring of s such that every character in t (including duplicates) is included in the window. If there is no such substring , return the empty string "" .

The testcases will be generated such that the answer is unique.

substring is a contiguous sequence of characters within the string.

 

Example 1:

Input: s = "ADOBECODEBANC", t = "ABC"
Output: "BANC"
Explanation: The minimum window substring "BANC" includes 'A', 'B', and 'C' from string t.

Example 2:

Input: s = "a", t = "a"
Output: "a"
Explanation: The entire string s is the minimum window.

Example 3:

Input: s = "a", t = "aa"
Output: ""
Explanation: Both 'a's from t must be included in the window.
Since the largest window of s only has one 'a', return empty string.

 

Constraints:

  • m == s.length
  • n == t.length
  • 1 <= m, n <= 105
  • s and t consist of uppercase and lowercase English letters.

 

Follow up:  Could you find an algorithm that runs in O(m + n) time?

解题思路

题中要求最好使用O(m + n)的时间复杂度来解决,所以Brute Force是行不通的。那么问题就来了,如何将嵌套循环优化为一次遍历?

这时候就该滑动窗口算法闪亮登场了。这个算法适合用来解决字符串/数组的子元素问题,可用来降低时间复杂度。抽象地说,整个字符串好比一个长长的推拉窗口,而这个窗口中间有两扇可移动的玻璃窗,我们可以这样做:

  1. 将一扇窗(右窗)从左端往右端移动
  2. 满足一定条件后,暂停移动右窗,将左窗从左端往右端移动
  3. 满足一定条件后,暂停移动左窗
  4. 重复上面的步骤,直到两扇窗移动到右边

回到本题, s 就是我们需要遍历的推拉窗,随着右窗的移动遍历 s 中的字符,暂停右窗的条件就是 t 中的字符都已出现,随着左窗移动,将 s 中的字符丢弃,当丢弃了 t 中的字符时,暂停左窗,继续右窗移动。

解题的关键在于

  1. 对字符串 t 建模,用数组或Map表示 t 中字符和出现的次数
  2. 遍历 s 时,当出现 t 中字符是,上述数组或Map的元素值-1,当计算后的元素值仍大于等于零时,表示有效计数
  3. 用计数器cnt表示 t 中字符在 s 中出现的有效次数,当cnt==t.length时表示 t 中字符都已出现
  4. t 中字符都已出现后,开始移动左窗

参考答案


    public String minWindow(String s, String t) {
        // 128 characters in ASCII
        int[] charCnt = new int[128];
        int left = 0;
        int foundCharCnt = 0;
        int minLeft = -1;
        int minLen = Integer.MAX_VALUE;

        // loop t, charCnt++ when char appears in t
        for (char c : t.toCharArray()) {
            ++charCnt[c];
        }

        // loop s
        for (int right = 0; right < s.length(); ++right) {
            // charCnt-- for each char, when char is in t (--charCnt >= 0) foundCharCnt++
            if (--charCnt[s.charAt(right)] >= 0) {
                foundCharCnt++;
            }
            // when all chars in t have been found (foundCharCnt == t.length())
            while (foundCharCnt == t.length()) {
                // record the minLen and minLeft
                if (minLen > right - left + 1) {
                    minLen = right - left + 1;
                    minLeft = left;
                }
                // if char at pointer left is in t (++charCnt > 0), foundCharCnt--
                if (++charCnt[s.charAt(left)] > 0) {
                    foundCharCnt--;
                }
                // shrink left pointer
                left++;
            }
        }
        return minLeft == -1 ? "" : s.substring(minLeft, minLeft + minLen);
    }

image.png

拓展训练

Given an array of integers of size ‘n’. Our aim is to calculate the maximum sum of ‘k’ consecutive elements in the array.

Input  : arr[] = {100, 200, 300, 400}
         k = 2
Output : 700
Input  : arr[] = {1, 4, 2, 10, 23, 3, 1, 0, 20}
         k = 4 
Output : 39
We get maximum sum by adding subarray {4, 2, 10, 23}
of size 4.
Input  : arr[] = {2, 3}
         k = 3
Output : Invalid
There is no subarray of size 3 as size of whole
array is 2.

详细解答参考下方资料链接

到作者的LeetCode专栏中看看,有没有其他感兴趣的问题吧!

juejin.cn/column/6997…

资料链接

原题 leetcode.com/problems/mi…

滑动窗口算法 levelup.gitconnected.com/an-introduc…

拓展训练解答 www.geeksforgeeks.org/window-slid…