【67.最小覆盖子串】

83 阅读1分钟

题目

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。

注意:

  • 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
  • 如果 s 中存在这样的子串,我们保证它是唯一的答案。

示例 1:

输入: s = "ADOBECODEBANC", t = "ABC"
输出: "BANC"
解释: 最小覆盖子串 "BANC" 包含来自字符串 t 的 'A''B''C'

题解

方式一:滑动窗口 + 哈希表

// 记录目标字符及数量
HashMap<Character, Integer> target = new HashMap<>();
// 记录当前窗口包含的字符及数量
HashMap<Character, Integer> cur = new HashMap<>();

public String minWindow(String s, String t) {

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

    int start = 0, end = 0, l = 0, minLen = Integer.MAX_VALUE;

    for (int r = 0; r < s.length(); r++) {
        char c = s.charAt(r);
        // 只记录target中存在的字符
        if (target.containsKey(c)) {
            cur.put(c, cur.getOrDefault(c, 0) + 1);
        }
        while (l <= r && check()) {
            if (r - l + 1 < minLen) {
                minLen = r - l + 1;
                start = l;
                end = l + minLen;
            }
            char cc = s.charAt(l);
            if (target.containsKey(cc)) {
                cur.put(cc, cur.getOrDefault(cc, 0) - 1);
            }
            l++;
        }
    }
    return s.substring(start, end);
}

boolean check() {
    // 和forEach相比可以提前结束
    Iterator iter = target.entrySet().iterator(); 
    while (iter.hasNext()) { 
        Map.Entry entry = (Map.Entry) iter.next(); 
        Character key = (Character) entry.getKey(); 
        Integer val = (Integer) entry.getValue(); 
        if (cur.getOrDefault(key, 0) < val) {
            return false;
        }
    } 
    return true;
}

方式二:滑动窗口 + 数组

A = 65 , z = 122

int[] target = new int[58];
int[] cur = new int[58];

public String minWindow(String s, String t) {

    for (char c: t.toCharArray()) {
        target[c - 'A']++;
    }

    int start = 0, end = 0, l = 0, minLen = Integer.MAX_VALUE;

    for (int r = 0; r < s.length(); r++) {
        char c = s.charAt(r);
        cur[c - 'A']++;
        while (l <= r && check()) {
            if (r - l + 1 < minLen) {
                minLen = r - l + 1;
                start = l;
                end = l + minLen;
            }
            char cc = s.charAt(l);
            cur[cc - 'A']--;
            l++;
        }
    }
    return s.substring(start, end);
}

boolean check() {
    for (int i = 0; i < target.length; i++) {
        if (target[i] > 0 && cur[i] < target[i]) {
            return false;
        }
    }
    return true;
}

总结

算法:滑动窗口
数据结构:哈希表数组