the two pointer technique for this problem is relatively simple:
we maintain two pointers i String S, left and right, at any time point, their is only one pointer moving. the right pointer is responsible for expand, and when we moving the right pointer to a certain point, the sliding window have all the desired characters, we cotract the substring by moving left pointer till we can’t move. then we get the shortest substring.

so the main problem we need to solve is, we keep moving the right pointer, but how do we know the sliding window has all the required characters?
and the other problem is: when we contract the substring to make it shorter, how do we know that when should we stop?
those two problems can be solved using hashmap. take a look at the unoptimized solution. it is kind of straight forward. we use lots of variable: left/right pointers, two hashmaps(one is to store the infor on t, and the other is to keep track of the sliding windows) and two vars(required and formed, first one is like first hashmap, uses to store static value, and the other one is like the second hashmap, which is to keep track of the unqiue char in sliding window)

class Solution {
    public String minWindow(String s, String t) {
        if (s == null || t == null || s.length() == 0 || t.length() == 0) {
            return null;
        }
        int m = s.length();
        int n = t.length();
        HashMap<Character, Integer> map = new HashMap<>();
        for (char c: t.toCharArray()) {
            map.put(c, map.getOrDefault(c, 0) + 1);
        } 
        //keep in mind that as long as the substring in s contains all the characters(even the number has to be the same) in t is valid
        int required = map.size();
        int l = 0; //left pointer
        int r = 0;
        
        int formed = 0;//it seems this var is the number of unique chars in t. only when formed == required, at that time, right pointer expanding stops
        
        //this hashmap keeps track of all the characters in substring
        HashMap<Character, Integer> windowCounts = new HashMap<Character, Integer>();
        
        int[] ans = {-1, 0, 0}; //{windowslength, left, right}, both are tight bound
        
        while (r < m) {
            char c = s.charAt(r);
            windowCounts.put(c, windowCounts.getOrDefault(c, 0) + 1);
            
            if (map.containsKey(c) && windowCounts.get(c).equals(map.get(c))) {
                formed++; //the means we have one char satisfied
            }
            
            //shrink phrase
            while (l <= r && formed == required) { //because we need to find the shortest, so we keeps moving
                c = s.charAt(l);
                if (ans[0] == -1 || r - l + 1 < ans[0]) { //if ans[0] havn't initialized or we have shorter solution
                    //update the info in ans
                    ans[0] = r - l + 1;
                    ans[1] = l;
                    ans[2] = r;
                }
                //and we also needs to update windowCounts
                windowCounts.put(c, windowCounts.get(c) - 1);
                //and update formed
                if (map.containsKey(c) && windowCounts.get(c).compareTo(map.get(c)) < 0) {
                    formed--;
                }
                //left pointer moves forward
                l++;
            }
            //update right pointer
            r++;
        }
        
        return ans[0] == -1? "": s.substring(ans[1], ans[2] + 1);
        
    }
}

used the same idea, let’s do it in a template way.
but there is some hazard things happens: I rewrtie the code but it just can’t pass the test and I can’t see the difference
My Code:

class Solution {
    public String minWindow(String s, String t) {
        if (s == null || t == null || s.length() == 0 || t.length() == 0) {
            return null;
        }
        int[] m = new int[128];
        for (char c: s.toCharArray()){
            m[c]++;
        }
        int start = 0;
        int end = 0;
        
        int counter = t.length();
        int size = s.length();
        
        int minStart = 0;
        int minLen = Integer.MAX_VALUE;//those two var are keep track of the shortest substring
        
        while (end < size) {
            char c = s.charAt(end);
            if (m[c] > 0) {
                counter--;
            }
            m[c]--; //even if m[c] is not > 0, still m[c]--
            end++;
            while(counter == 0) { //when counter is 0, then we found a valid sliding, start shrinking
                if (end - start < minLen) { //because end is already incremented
                    minLen = end - start;
                    minStart = start;
                }
                char c1 = s.charAt(start); //we iterate to current start
                m[c1]++;
                if (m[c1] > 0) { //if the char we are about to abandone exists in t,
                    counter++; //that means we have to deal with it later, shrink's over
                }
                start++;
            }
        }
        
        return minLen == Integer.MAX_VALUE? "": s.substring(minStart, minStart + minLen);
        
    }
   
}

code in solution:

public String minWindow(String s, String t) {
    String result = "";
    if(s==""||t.length()>s.length())
        return result;
    int[] map = new int[128];
    int start = 0;
    int minStart = 0;
    int end = 0;
    int count = t.length();
    int minLength = Integer.MAX_VALUE;
    for(char temp:t.toCharArray()){
        map[temp]++;
    }
    while(end<s.length()){
        if(map[s.charAt(end)]>0)
            count--;
        map[s.charAt(end)]--;
        end++;
        while(count==0){
			if (end - start < minLength) {
				minStart = start;
				minLength = end - start;
			}
			map[s.charAt(start)]++;
			if (map[s.charAt(start)] > 0)
				count++;
			start++;
        }
    }
    return (minLength==Integer.MAX_VALUE)?"":s.substring(minStart, minStart+minLength);
}