import java.util.HashMap;

public class MinWindowSubstring {
    //方法一:暴力法,枚举s中所有子串
    public String minWindow(String s, String t) {
        //定义最小字串,保存结果,初始为空字符串
        String minSubString = "";

        //定义一个HashMap 保存t中字符出现的频次
        HashMap<Character, Integer> tCharFrequency = new HashMap<>();

        //统计t中字符频次
        for (int i = 0; i <t.length() ; i++) {
            char c = t.charAt(i);
            int count = tCharFrequency.getOrDefault(c,0);
            tCharFrequency.put(c,count+1);
        }

        // 接下来在s中搜索覆盖子串
        //遍历所有字符,作为当前子串的起始位置
        for (int i = 0; i < s.length(); i++) {
            //遍历i之后不小于t长度的位置,作为字串的结束位置
            for (int j = i+t.length(); j <=s.length(); j++) {
                //统计s字串中每个字符出现的频次
                //定义一个HashMap 保存s字串中字符出现的频次
                HashMap<Character, Integer> subStrCharFrequency = new HashMap<>();

                //统计t中字符频次
                for (int k = i; k <j ; k++) {
                    char c = s.charAt(k);
                    int count = subStrCharFrequency.getOrDefault(c,0);
                    subStrCharFrequency.put(c,count+1);
                }

                //如果当前子串符合覆盖子串的要求,并且比之前的最小子串要短,就替换
                if(check(tCharFrequency,subStrCharFrequency)&&(minSubString.equals("")||j-i<minSubString.length())){
                    minSubString = s.substring(i,j);
                }
            }
        }
        return minSubString;
    }

    //提炼一个方法,用于检查当前子串是否是一个覆盖t的子串
    public boolean check(HashMap<Character,Integer> tFreq,HashMap<Character,Integer> subStringFreq){
        //遍历t中的字符,看在substring中是否包含
        for (char c:tFreq.keySet()) {
            if(subStringFreq.getOrDefault(c,0)<tFreq.get(c)){
                return false;
            }
        }
        return true;
    }

    //方法二:滑动窗口
    public String minWindow2(String s, String t) {
        //定义最小字串,保存结果,初始为空字符串
        String minSubString = "";

        //定义一个HashMap 保存t中字符出现的频次
        HashMap<Character, Integer> tCharFrequency = new HashMap<>();

        //统计t中字符频次
        for (int i = 0; i <t.length() ; i++) {
            char c = t.charAt(i);
            int count = tCharFrequency.getOrDefault(c,0);
            tCharFrequency.put(c,count+1);
        }

        //定义左右指针指向滑动窗口的起始和结束位置
        int start = 0;
        int end  = t.length();
        while (end<=s.length()){
            //定义一个HashMap 保存s字串中字符出现的频次
            HashMap<Character, Integer> subStrCharFrequency = new HashMap<>();

            //统计t中字符频次
            for (int k = start; k <end ; k++) {
                char c = s.charAt(k);
                int count = subStrCharFrequency.getOrDefault(c,0);
                subStrCharFrequency.put(c,count+1);
            }

            //如果当前子串符合覆盖子串的要求,并且比之前的最小子串要短,就替换
            if(check(tCharFrequency,subStrCharFrequency)){
                if((minSubString.equals("")||end-start<minSubString.length())){
                    minSubString = s.substring(start,end);
                }
                //只要是覆盖子串,就移动初始位置
                start++;
            }else{
                //如果不是覆盖子串,需要扩大窗口,继续寻找新的子串
                end++;
            }
        }
        return minSubString;
    }
    //方法三:滑动窗口优化
    public String minWindow3(String s, String t) {
        //定义最小字串,保存结果,初始为空字符串
        String minSubString = "";

        //定义一个HashMap 保存t中字符出现的频次
        HashMap<Character, Integer> tCharFrequency = new HashMap<>();

        //统计t中字符频次
        for (int i = 0; i < t.length(); i++) {
            char c = t.charAt(i);
            int count = tCharFrequency.getOrDefault(c, 0);
            tCharFrequency.put(c, count + 1);
        }

        //定义左右指针,指向滑动窗口的起始和结束位置
        int start = 0,end = 1;
        //定义一个hashmap,保存s子串中字符出现的频次
        HashMap<Character, Integer> subStrCharFrequency = new HashMap<>();

        while (end<=s.length()){

            //end增加之后,新增的字符
            char newChar = s.charAt(end-1);

            if(tCharFrequency.containsKey(newChar)){
                subStrCharFrequency.put(newChar,subStrCharFrequency.getOrDefault(newChar,0)+1);
            }


            //如果当前子串符合覆盖子串的要求,并且比之前的最小子串要短,就替换
            while (check(tCharFrequency,subStrCharFrequency)&&start<end){
                if((minSubString.equals("")||end-start<minSubString.length())){
                    minSubString = s.substring(start,end);
                }
                //对要删除的字符,频次减1
                char removeChar = s.charAt(start);

                if(tCharFrequency.containsKey(removeChar)){
                    subStrCharFrequency.put(removeChar,subStrCharFrequency.getOrDefault(removeChar,0)-1);
                }
                //只要是覆盖子串,就移动初始位置
                start++;
            }
            //如果不是覆盖子串,需要扩大窗口,继续寻找新的子串
            end++;
        }
        return minSubString;
    }

    //方法四:进一步优化
    public String minWindow4(String s, String t) {
        //定义最小字串,保存结果,初始为空字符串
        String minSubString = "";

        //定义一个HashMap 保存t中字符出现的频次
        HashMap<Character, Integer> tCharFrequency = new HashMap<>();

        //统计t中字符频次
        for (int i = 0; i < t.length(); i++) {
            char c = t.charAt(i);
            int count = tCharFrequency.getOrDefault(c, 0);
            tCharFrequency.put(c, count + 1);
        }

        //定义左右指针,指向滑动窗口的起始和结束位置
        int start = 0,end = 1;
        //定义一个hashmap,保存s子串中字符出现的频次
        HashMap<Character, Integer> subStrCharFrequency = new HashMap<>();

        //定义一个”子串贡献值“变量,统计t中的字符在子串中出现了多少
        int charCount = 0;

        while (end<=s.length()){

            //end增加之后,新增的字符
            char newChar = s.charAt(end-1);

            if(tCharFrequency.containsKey(newChar)){
                subStrCharFrequency.put(newChar,subStrCharFrequency.getOrDefault(newChar,0)+1);
                //如果子串中频次小于t中频次,当前频次有贡献
                if(subStrCharFrequency.get(newChar)<=tCharFrequency.get(newChar)){
                    charCount++;
                }
            }


            //如果当前子串符合覆盖子串的要求,并且比之前的最小子串要短,就替换
            while (charCount==t.length()&&start<end){
                if((minSubString.equals("")||end-start<minSubString.length())){
                    minSubString = s.substring(start,end);
                }
                //对要删除的字符,频次减1
                char removeChar = s.charAt(start);

                if(tCharFrequency.containsKey(removeChar)){
                    subStrCharFrequency.put(removeChar,subStrCharFrequency.getOrDefault(removeChar,0)-1);
                    //如果子串中的频次如果不够t中的频次,贡献值减少
                    if(subStrCharFrequency.get(removeChar)<tCharFrequency.get(removeChar)){
                        charCount--;
                    }
                }
                //只要是覆盖子串,就移动初始位置
                start++;
            }
            //如果不是覆盖子串,需要扩大窗口,继续寻找新的子串
            end++;
        }
        return minSubString;
    }

    public static void main(String[] args) {
        String s = "ADOBECODEBANC", t = "ABC";
        MinWindowSubstring minWindowSubstring = new MinWindowSubstring();
        String s1 = minWindowSubstring.minWindow4(s, t);
        System.out.println(s1);
    }
}