package com.freetymekiyan.algorithms.level.hard; /** * Given a string S and a string T, find the minimum window in S which will contain all the characters in T in * complexity O(n). * <p> * For example, * S = "ADOBECODEBANC" * T = "ABC" * Minimum window is "BANC". * <p> * Note: * If there is no such window in S that covers all characters in T, return the empty string "". * <p> * If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in * S. * <p> * Company Tags: LinkedIn, Uber, Facebook * Tags: Hash Table, Two Pointers, String * Similar Problems: (H) Substring with Concatenation of All Words, (M) Minimum Size Subarray Sum, (H) Sliding Window * Maximum */ public class MinWindowSubstring { /** * Hash Table. Two Pointers. * <p> * 1. Use two pointers: start and end to represent a window. * 2. Move end to find a valid window. * 3. When a valid window is found, move start to find a smaller window. * <p> * Use map to store count of each character in a String. * One for T and the other for the window in S. * Use two pointers to traverse S, the String in between is the window. * The pointer in the front moves when the window doesn't cover all characters in T. * The pointer in the back moves when the window covers all characters, to get as smaller window as possible. * Stop when the front pointer reaches the end. * We need a variable to remember the minimum window length during this procedure. * How to know whether T is covered by the window with two maps? Comparing them would be costly. * Use a variable called letterCnt to keep track of the letters covered. * When the character is in T, and the count in window is less than or equal to the count in T, increase letterCnt. * When letterCnt >= length of T, we know that T is fully covered. */ public String minWindow(String s, String t) { String res = ""; int[] tCnt = new int[256]; // Assuming charset size 256. // Initialize substring character count map. for (char c : t.toCharArray()) { tCnt[c]++; } int minLen = Integer.MAX_VALUE; int count = t.length(); // Counter for whether the window is valid. for (int start = 0, end = 0; end < s.length(); end++) { /* * If current character is in t, the value of it should be > 0. * If it is > 0, it is a match, decrement counter by 1. * Decrement the character count in map as well. */ if (tCnt[s.charAt(end)]-- > 0) { count--; } while (count == 0) { // If count is 0, all characters in t are matched. // Update minimum length and string. if (end - start + 1 < minLen) { minLen = end - start + 1; res = s.substring(start, end + 1); } /* * Move start. * Remove the character at the start of the window from t's map by increasing it's value. * If it's value becomes > 0, there is some character in t that is removed. */ if (++tCnt[s.charAt(start++)] > 0) { count++; } } } return res; } /** * https://discuss.leetcode.com/topic/30941/here-is-a-10-line-template-that-can-solve-most-substring-problems * For most substring problem, we are given a string and need to find a substring of it which satisfy some * restrictions. * A general way is to use a map assisted with two pointers. The template is given below. */ int findSubstring(String s) { int[] map = new int[128]; int counter; // Check whether the substring is valid int begin = 0, end = 0; // Two pointers, one point to tail and one head int d = Integer.MAX_VALUE; // The length of substring // for () { /* Initialize the hash map here */ } while (end < s.length()) { // Traverse s // if (map[s[end++]]-- ?) { /* modify counter here */ } // while (/* counter condition */) { /* update d here if finding minimum*/ //increase begin to make it invalid/valid again // if (map[s[begin++]]++ ?) { /*modify counter here*/ } //} /* update d here if finding maximum*/ } return d; } }