最小覆盖子串

/**
 * 【最小覆盖子串】
 * 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 "" 。
 * 输入:s = "ADOBECODEBANCBDB", t = "ABBC"
 * 输出:"BANC"
 * https://leetcode-cn.com/problems/minimum-window-substring/solution/tong-su-qie-xiang-xi-de-miao-shu-hua-dong-chuang-k/
 * 分析:
 * 滑动窗口——右指针右移增大窗口,左指针右移缩小窗口
 * hash表装条件及其个数,先扩大窗口满足条件,再缩小窗口找到第1个解;然后leftIdx + 1,肯定不再满足条件,继续扩大窗口找解,直到最后
 * 如何判定满足条件:扩大窗口——对条件计数器减1,减到0就停
 * 如何找最优解:左指针碰到第一个条件字母就停止
 *
 * @author xuan
 * @create 2021-10-11 17:13
 **/
public class ZuiXiaoFuGaiZiChuan {

    public static void main(String[] args) {
        String s = "ABCOBANCBA";
        String t = "BBC";
        System.out.println(minWindow(s, t));
    }

    public static String minWindow(String s, String t) {
        if ("".equals(s) || s.length() < t.length()) {
            return "";
        }
        //处理条件hash表:作为计数表使用,value表示仍需要的字符数,如果小于等于0表示该字符数量够了
        Map tMap = new HashMap<>();
        for (int i = 0; i < t.length(); i++) {
            Character key = t.charAt(i);
            tMap.put(key, tMap.getOrDefault(key, 0) + 1);
        }

        int leftIdx = 0;
        int rightIdx = 0;
        String minWindow = "";
        //条件计数器: 只统计
        int tCount = t.length();
        while (leftIdx < s.length()) {
            //增大窗口
            for (; rightIdx < s.length(); rightIdx++) {
                Character key = s.charAt(rightIdx);
                if (tMap.containsKey(key)) {
                    tMap.put(key, tMap.get(key) - 1);
                    if (tMap.get(key) >= 0) {
                        tCount--;
                    }
                }
                if (tCount == 0) {
                    break;
                }
            }
            if (tCount > 0) {
                return minWindow;
            }

            //缩小窗口
            while (leftIdx < s.length()) {
                Character key = s.charAt(leftIdx);
                if (!tMap.containsKey(key)) {
                    leftIdx++;
                    continue;
                }
                if (tMap.get(key) == 0) {
                    //表示该字符刚好够用,不能移除
                    break;
                } else {
                    //小于0就加1,表示窗口内还够用
                    tMap.put(key, tMap.get(key) + 1);
                    leftIdx++;
                }
            }

            //rightIdx可能到最大边界了
            int endIndex = rightIdx + 1 > s.length() ? s.length() : rightIdx + 1;
            //substring:含左不含右
            String tmpMinWindow = s.substring(leftIdx, endIndex);
            if ("".equals(minWindow)) {
                //首个解
                minWindow = tmpMinWindow;
            } else if (tmpMinWindow.length() < minWindow.length()) {
                //之前得到过解
                minWindow = tmpMinWindow;
            }

            //leftIdx + 1打破条件,重复上述步骤:从增大窗口开始
            Character key = s.charAt(leftIdx++);
            //左边界的计数表和tCount都加1
            tMap.put(key, tMap.get(key) + 1);
            tCount++;
            //rightIdx如果不加1,则下一个循环的"增大窗口"步骤,会把rightIdx的元素再减一次
            rightIdx++;
        }
        return minWindow;
    }
}

你可能感兴趣的:(最小覆盖子串)