「力扣」第 76 題:最小覆蓋子串(滑動窗口例題)

可能可以參考的模板寫法:

public class Solution {

    public String minWindow(String s, String t) {
        // 起始的時候,都位於 0,同方向移動
        int left = 0;
        int right = 0;

        // 根據題意設計狀態變量
        while (right < len) {
            // 在 right 右移的過程中維護狀態變量的定義
            if () {
                // 維護狀態變量的定義
            }
            // 右邊界右移 1 格
            right++;

            // 狀態變量滿足一定條件,窗口是一個可行解
            while ( ) {
                // 在 left 右移的過程中維護狀態變量的定義
                if () {
                    // 維護狀態變量的定義
                }
                // 左邊界右移 1 格
                left++;
            }
        }
        return 需要的結果變量;
    }
}

使用字符頻數數組,用加法:

加法 continue 的寫法;

public class Solution {

    public String minWindow(String s, String t) {
        int sLen = s.length();
        int tLen = t.length();
        if (sLen == 0 || tLen == 0 || sLen < tLen) {
            return "";
        }

        // 字符頻數數組,用哈希表也可以,用 128 是測試測出來的,後臺數據的字符 ascii 值最多到 `z`(122)
        int[] winFreq = new int[128];
        int[] tFreq = new int[128];

        // 滑動窗口內部不同字符的個數
        int distance = 0;

        char[] charArrayS = s.toCharArray();
        char[] charArrayT = t.toCharArray();

        for (char c : charArrayT) {
            tFreq[c]++;
        }

        int minLen = sLen + 1;
        // 與 minLen 對應的起始位置的下標
        int begin = 0;


        int left = 0;
        int right = 0;

        // 以下才是滑動窗口的核心邏輯
        // 循環不變量:s[left..right) 包含 T 中所有的字符
        while (right < sLen) {

            if (tFreq[charArrayS[right]] == 0) {
                right++;
                continue;
            }
            // 只關心在 T 中出現的字符
            if (winFreq[charArrayS[right]] < tFreq[charArrayS[right]]) {
                distance++;
            }
            winFreq[charArrayS[right]]++;
            // 看完了 right 位置的字符,並且統計了相關信息以後,指針右移
            right++;
            while (distance == tLen) {
                if (tFreq[charArrayS[left]] == 0) {
                    left++;
                    continue;
                }
                // 只關心在 T 中出現的字符
                if (winFreq[charArrayS[left]] == tFreq[charArrayS[left]]) {
                    distance--;
                }
                winFreq[charArrayS[left]]--;

                // 計算最小長度,只要在 left++ 之前都行
                // 把區間定義成左閉右開的原因是,長度值 = right - left 不用 + 1
                if (right - left < minLen) {
                    minLen = right - left;
                    begin = left;
                }
                left++;
            }
        }

        if (minLen == sLen + 1) {
            return "";
        }
        return s.substring(begin, begin + minLen);
    }
}
public class Solution {

    public String minWindow(String s, String t) {
        int sLen = s.length();
        int tLen = t.length();
        if (sLen == 0 || tLen == 0 || sLen < tLen) {
            return "";
        }

        char[] charArrayS = s.toCharArray();
        char[] charArrayT = t.toCharArray();

        int[] winFreq = new int[128];
        int[] tFreq = new int[128];

        // 窗口內包含 T 的字符的個數(字符頻數大於 T 中的字符頻數時,不再增加)
        int distance = 0;

        for (char charT : charArrayT) {
            tFreq[charT]++;
        }

        int begin = 0;
        int minLen = sLen + 1;
        int left = 0;
        int right = 0;

        // [left, right) 每一輪開始之前,right 這個下標左邊的元素我們都看過了
        while (right < sLen) {
            // 只關心 T 中存在的字符
            char rightChar = charArrayS[right];
            if (tFreq[rightChar] == 0) {
                right++;
                continue;
            }
            if (winFreq[rightChar] < tFreq[rightChar]) {
                distance++;
            }

            winFreq[rightChar]++;

            // 到了下一輪
            right++;

            while (distance == tLen) {
                // 這裏位置還可以靠後,只要在 left 之前都是沒有問題的
                if (right - left < minLen) {
                    minLen = right - left;
                    begin = left;
                }

                char leftChar = charArrayS[left];
                if (tFreq[leftChar] == 0) {
                    left++;
                    continue;
                }

                if (winFreq[leftChar] == tFreq[leftChar]) {
                    distance--;
                }
                winFreq[leftChar]--;

                left++;
            }
        }

        if (minLen == sLen + 1) {
            return "";
        }
        return s.substring(begin, begin + minLen);
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        String S = "ADOBECODEBANC";
        String T = "ABC";
        String res = solution.minWindow(S, T);
        System.out.println(res);
    }
}

加法,不 continue 的寫法;

public class Solution {

    public String minWindow(String s, String t) {
        int sLen = s.length();
        int tLen = t.length();
        if (sLen == 0 || tLen == 0 || sLen < tLen) {
            return "";
        }

        int[] tFreq = new int[128];
        int[] winFreq = new int[128];

        int distance = 0;
        for (char tChar : t.toCharArray()) {
            tFreq[tChar]++;
        }


        int left = 0;
        int right = 0;
        int minLen = sLen + 1;
        int start = 0;

        char[] sCharArray = s.toCharArray();

        while (right < sLen) {
            char rightChar = sCharArray[right];
            if (tFreq[rightChar] > 0) {
                if (winFreq[rightChar] < tFreq[rightChar]) {
                    distance++;
                }
            }
            winFreq[rightChar]++;

            right++;
            while (distance == tLen) {
                char leftChar = sCharArray[left] ;
                if (tFreq[leftChar] > 0) {

                    if (winFreq[leftChar] == tFreq[leftChar]) {
                        distance--;
                    }
                    winFreq[leftChar]--;
                }

                if (right - left < minLen) {
                    minLen = right - left;
                    start = left;
                }
                left++;
            }
        }

        if (minLen == sLen + 1) {
            return "";
        }
        return s.substring(start, start + minLen);
    }
}

加法,用哈希表

import java.util.HashMap;
import java.util.Map;

public class Solution3 {

    public String minWindow(String s, String t) {
        int sLen = s.length();
        int tLen = t.length();
        if (sLen == 0 || tLen == 0 || sLen < tLen) {
            return "";
        }

        Map<Character, Integer> winFreq = new HashMap<>();
        Map<Character, Integer> tFreq = new HashMap<>();

        for (Character c : t.toCharArray()) {
            tFreq.put(c, tFreq.getOrDefault(c, 0) + 1);
        }

        int minLen = sLen + 1;
        int begin = 0;

        int left = 0;
        int right = 0;
        int distance = 0;

        while (right < sLen) {
            Character rightChar = s.charAt(right);
            if (tFreq.containsKey(rightChar)) {
                // winFreq.get(rightChar) == null 這一步容易忽略
                if (winFreq.get(rightChar) == null || winFreq.get(rightChar) < tFreq.get(rightChar)) {
                    distance++;
                }
                
                winFreq.put(rightChar, winFreq.getOrDefault(rightChar, 0) + 1);
            }

            right++;

            while (distance == tLen) {
                if (right - left < minLen) {
                    begin = left;
                    minLen = right - left;
                }

                Character leftChar = s.charAt(left);
                if (tFreq.containsKey(leftChar)) {
                    // 注意:包裝類型使用 equals
                    if (winFreq.get(leftChar).equals(tFreq.get(leftChar))) {
                        distance--;
                    }
                    winFreq.put(leftChar, winFreq.getOrDefault(leftChar, 0) - 1);
                }
                left++;
            }
        }
        return minLen == sLen + 1 ? "" : s.substring(begin, begin + minLen);
    }
}

數組,減法

public class Solution {

    public String minWindow(String s, String t) {
        int sLen = s.length();
        int tLen = t.length();
        if (sLen == 0 || tLen == 0 || sLen < tLen) {
            return "";
        }

        int[] tFreq = new int[128];

        // T 滑動窗口內部還差多少個「不同」字符能覆蓋 T
        int distance = tLen;

        char[] charArrayS = s.toCharArray();
        char[] charArrayT = t.toCharArray();

        for (char c : charArrayT) {
            tFreq[c]++;
        }

        int minLen = sLen + 1;
        // 與 minLen 對應的起始位置的下標
        int begin = 0;

        int left = 0;
        int right = 0;

        // 以下才是滑動窗口的核心邏輯
        // 循環不變量:s[left..right) 包含 T 中所有的字符
        while (right < sLen) {
            // 只關心在 T 中出現的字符
            if (tFreq[charArrayS[right]] > 0) {
                distance--;
            }
            tFreq[charArrayS[right]]--;
            // 看完了 right 位置的字符,並且統計了相關信息以後,指針右移
            right++;

            while (distance == 0) {
                // 把區間定義成左閉右開的原因是,長度值 = right - left 不用 + 1
                if (right - left < minLen) {
                    minLen = right - left;
                    begin = left;
                }

                // 只關心在 T 中出現的字符
                tFreq[charArrayS[left]]++;
                if (tFreq[charArrayS[left]] > 0) {
                    distance++;
                }
                left++;
            }
        }

        if (minLen == sLen + 1) {
            return "";
        }
        return s.substring(begin, begin + minLen);
    }
}
public class Solution {

    public String minWindow(String s, String t) {
        int sLen = s.length();
        int tLen = t.length();
        if (sLen == 0 || tLen == 0 || sLen < tLen) {
            return "";
        }

        char[] charArrayS = s.toCharArray();
        char[] charArrayT = t.toCharArray();

        int distance = 0;
        int begin = 0;
        int minLen = sLen + 1;

        int[] tFreq = new int[128];
        for (char c : charArrayT) {
            tFreq[c]++;
        }

        for (int left = 0, right = 0; right < sLen; right++) {
            tFreq[charArrayS[right]]--;
            if (tFreq[charArrayS[right]] >= 0) {
                distance++;
            }
            
            while (distance == tLen) {
                if (minLen > right - left + 1) {
                    minLen = right - left + 1;
                    begin = left;
                }
                tFreq[charArrayS[left]]++;
                if (tFreq[charArrayS[left]] > 0) {
                    distance--;
                }
                left++;
            }
        }

        if (minLen == sLen + 1) {
            return "";
        }
        return s.substring(begin, begin + minLen);
    }
}

哈希表,用減法

import java.util.HashMap;
import java.util.Map;

public class Solution {

    public String minWindow(String s, String t) {
        int sLen = s.length();
        int tLen = t.length();
        if (sLen == 0 || tLen == 0 || sLen < tLen) {
            return "";
        }

        Map<Character, Integer> tFreq = new HashMap<>();
        char[] charArrayT = t.toCharArray();
        for (char c : charArrayT) {
            tFreq.put(c, tFreq.getOrDefault(c, 0) + 1);
        }
        int distance = tLen;

        int minLen = sLen + 1;
        int left = 0;
        int right = 0;
        int begin = 0;

        char[] charArrayS = s.toCharArray();
        while (right < sLen) {

            Integer rightFreq = tFreq.get(charArrayS[right]);
            if (rightFreq == null) {
                right++;
                continue;
            }

            if(rightFreq > 0){
                distance--;

            }
            tFreq.put(charArrayS[right], rightFreq - 1);
            right++;

            while (distance == 0) {
                if (right - left < minLen) {
                    minLen = right - left;
                    begin = left;
                }


                Integer leftFreq = tFreq.get(charArrayS[left]);
                if (leftFreq == null) {
                    left++;
                    continue;
                }

                leftFreq++;
                tFreq.put(charArrayS[left], leftFreq);
                if (leftFreq > 0) {
                    distance++;
                }


                left++;
            }
        }

        if (minLen == sLen + 1) {
            return "";
        }
        return s.substring(begin, begin + minLen);
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        String S = "ADOBECODEBANC";
        String T = "ABC";
        String minWindow = solution.minWindow(S, T);
        System.out.println(minWindow);
    }
}

參考資料:

  • https://www.cnblogs.com/grandyang/p/4340948.html

比較麻煩的加法:

public class Solution {

    // 最新版代碼:加法

    public String minWindow(String s, String t) {
        int sLen = s.length();
        int tLen = t.length();
        if (sLen == 0 || tLen == 0 || sLen < tLen) {
            return "";
        }

        // 字符頻數數組,用哈希表也可以,用 128 是測試測出來的,後臺數據的字符 ascii 值最多到 `z`(122)
        int[] winFreq = new int[128];
        int[] tFreq = new int[128];

        // 滑動窗口內部不同字符的個數
        int winCount = 0;
        // T 中不同字符的個數
        int tCount = 0;

        char[] charArrayS = s.toCharArray();
        char[] charArrayT = t.toCharArray();

        for (char c : charArrayT) {
            if (tFreq[c] == 0) {
                tCount++;
            }
            tFreq[c]++;
        }

        int minLen = sLen + 1;
        // 與 minLen 對應的起始位置的下標
        int begin = 0;


        int left = 0;
        int right = 0;

        // 以下才是滑動窗口的核心邏輯
        // 循環不變量:s[left..right) 包含 T 中所有的字符
        while (right < sLen) {
            // 只關心在 T 中出現的字符
            if (tFreq[charArrayS[right]] == 0) {
                right++;
                continue;
            }

            winFreq[charArrayS[right]]++;
            if (winFreq[charArrayS[right]] == tFreq[charArrayS[right]]) {
                winCount++;
            }
            // 看完了 right 位置的字符,並且統計了相關信息以後,指針右移
            right++;
            while (winCount == tCount) {
                // 只關心在 T 中出現的字符
                if (tFreq[charArrayS[left]] == 0) {
                    left++;
                    continue;
                }

                winFreq[charArrayS[left]]--;
                if (winFreq[charArrayS[left]] < tFreq[charArrayS[left]]) {
                    winCount--;
                }

                // 計算最小長度,只要在 left++ 之前都行
                // 把區間定義成左閉右開的原因是,長度值 = right - left 不用 + 1
                if (right - left < minLen) {
                    minLen = right - left;
                    begin = left;
                }
                left++;
            }
        }

        if (minLen == sLen + 1) {
            return "";
        }
        return s.substring(begin, begin + minLen);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章