「力扣」第 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);
    }
}

猜你喜欢

转载自blog.csdn.net/lw_power/article/details/106169049