https://leetcode-cn.com/problems/minimum-window-substring/

    1. class Solution {
    2. public String minWindow(String s, String t) {
    3. // 时间:O(N)
    4. // 滑动窗口:枚举 s 中 可能覆盖 t 的子串
    5. String resString = "";
    6. // 定义一个 HashMap 保存频次
    7. HashMap<Character, Integer> frequencyMap = new HashMap<>();
    8. // 统计 t 中的字符
    9. for (int i = 0; i < t.length(); i++) {
    10. char c = t.charAt(i);
    11. int count = frequencyMap.getOrDefault(c, 0);
    12. frequencyMap.put(c, count + 1);
    13. }
    14. // 定义左右指针指向滑动窗口的起始、结束位置(左闭右开)
    15. int start = 0, end = 1;
    16. // 统计子串每个字符出现的频次
    17. HashMap<Character, Integer> subStringFrequencyMap = new HashMap<>();
    18. while (end <= s.length()) {
    19. // end 增加之后,新增的字符
    20. char newChar = s.charAt(end - 1);
    21. // 新增字符频次加一
    22. if (frequencyMap.containsKey(newChar)) {
    23. // t 串中有该字符才判断
    24. subStringFrequencyMap.put(newChar, subStringFrequencyMap.getOrDefault(newChar, 0) + 1);
    25. }
    26. // 如果当前子串符合覆盖子串要求,并且比之前的最小子串要短,就替换
    27. while (check(frequencyMap, subStringFrequencyMap) && start < end) {
    28. if ((resString.equals("") || (end - start) < resString.length())) {
    29. resString = s.substring(start, end);
    30. }
    31. // 对要删除的字符频次减一
    32. char removeChar = s.charAt(start);
    33. if (frequencyMap.containsKey(removeChar)) {
    34. // t 串中有该字符才判断
    35. subStringFrequencyMap.put(removeChar, subStringFrequencyMap.getOrDefault(removeChar, 0) - 1);
    36. }
    37. // 不管是不是最小子串,左指针都需要移动,为了缩小窗口,寻找局部最优解
    38. start++;
    39. }
    40. // 不是覆盖子串,就要扩大窗口,寻找新的子串
    41. end++;
    42. }
    43. return resString;
    44. }
    45. // 用于检查当前子串是否是一个覆盖 t 的子串
    46. private boolean check(HashMap<Character, Integer> tFreq, HashMap<Character, Integer> subFreq) {
    47. // 遍历 t 中每个字符的频次,与 subSting 进行比较
    48. // 查看 t 中出现的每个字符在 subFreq 中出现的次数,一旦有不相等的情况出现(也不可能大于),就返回 false
    49. for (Character character : tFreq.keySet()) {
    50. if (subFreq.getOrDefault(character, 0) < tFreq.get(character)) {
    51. return false;
    52. }
    53. }
    54. return true;
    55. }
    56. }
    1. class Solution {
    2. public String minWindow(String s, String t) {
    3. // 时间:O(N),但是实际会比上一个方法快得多
    4. // 滑动窗口:枚举 s 中 可能覆盖 t 的子串
    5. String resString = "";
    6. // 定义一个 HashMap 保存频次
    7. HashMap<Character, Integer> frequencyMap = new HashMap<>();
    8. // 统计 t 中的字符
    9. for (int i = 0; i < t.length(); i++) {
    10. char c = t.charAt(i);
    11. int count = frequencyMap.getOrDefault(c, 0);
    12. frequencyMap.put(c, count + 1);
    13. }
    14. // 定义左右指针指向滑动窗口的起始、结束位置(左闭右开)
    15. int start = 0, end = 1;
    16. // 统计子串每个字符出现的频次
    17. HashMap<Character, Integer> subStringFrequencyMap = new HashMap<>();
    18. // 定义一个"贡献值"变量,统计 t 中的字符在子串中出现了多少
    19. int targetCharCount = 0;
    20. while (end <= s.length()) {
    21. // end 增加之后,新增的字符
    22. char newChar = s.charAt(end - 1);
    23. // 新增字符频次加一
    24. if (frequencyMap.containsKey(newChar)) {
    25. // t 串中有该字符才判断
    26. subStringFrequencyMap.put(newChar, subStringFrequencyMap.getOrDefault(newChar, 0) + 1);
    27. // 频次加一,还要看是否有贡献,如果子串中频次小于 t 中频次,当前字符就有贡献
    28. if (subStringFrequencyMap.get(newChar) <= frequencyMap.get(newChar)) {
    29. targetCharCount++;
    30. }
    31. }
    32. // 如果当前子串符合覆盖子串要求,即贡献值达到了 t 子串的数值。并且比之前的最小子串要短,就替换
    33. while (targetCharCount == t.length() && start < end) {
    34. if ((resString.equals("") || (end - start) < resString.length())) {
    35. resString = s.substring(start, end);
    36. }
    37. // 对要删除的字符频次减一
    38. char removeChar = s.charAt(start);
    39. if (frequencyMap.containsKey(removeChar)) {
    40. // t 串中有该字符才判断
    41. subStringFrequencyMap.put(removeChar, subStringFrequencyMap.getOrDefault(removeChar, 0) - 1);
    42. // 如果子串中的频次不够 t 重的频次,此时相当于减少了一个真正有贡献的 char,贡献值减一
    43. if (subStringFrequencyMap.get(removeChar) < frequencyMap.get(removeChar)) {
    44. targetCharCount--;
    45. }
    46. }
    47. // 不管是不是最小子串,左指针都需要移动,为了缩小窗口,寻找局部最优解
    48. start++;
    49. }
    50. // 不是覆盖子串,就要扩大窗口,寻找新的子串
    51. end++;
    52. }
    53. return resString;
    54. }
    55. }
    class Solution {
        public String minWindow(String s, String t) {
    String res = "";
            HashMap<Character, Integer> targetMap = new HashMap<>();
            for (int i = 0; i < t.length(); i++) {
                targetMap.put(t.charAt(i), targetMap.getOrDefault(t.charAt(i), 0) + 1);
            }
            int start = 0, end = 1;
            HashMap<Character, Integer> subStringMap = new HashMap<>();
            int subStringCountVal = 0;
            while (end <= s.length()) {
                // 新增字符的操作
                char newChar = s.charAt(end - 1);
                if (targetMap.containsKey(newChar)) {
                    subStringMap.put(newChar, subStringMap.getOrDefault(newChar, 0) + 1);
                    // 遍历到到字串中到当前char的频次小于或等于目标字串中的频次时,都是有贡献的
                    // 因为是目标字串中的频次值先加一
                    if (subStringMap.get(newChar) <= targetMap.get(newChar)) {
                        subStringCountVal++;
                    }
                }
                // 贡献值达到了才会有 start++ 的左缩减操作
                while (subStringCountVal == t.length() && start < end) {
                    if (res.equals("") || (end - start) < res.length()) {
                        res = s.substring(start, end);
                    }
                    char removeChar = s.charAt(start);
                    if (targetMap.containsKey(removeChar)) {
                        // 尝试缩减字符
                        subStringMap.put(removeChar, subStringMap.get(removeChar) - 1);
                        // 如果字符缩减之后,频次出现问题,贡献值变小
                        if (subStringMap.get(removeChar) < targetMap.get(removeChar)) {
                            subStringCountVal--;
                        }
                    }
                    start++;
                }
                end++;
            }
            return res;
        }
    }