使用滑动窗口法来解决substring问题的模板如下:
1 public class Solution { 2 public List<Integer> slidingWindowTemplateByHarryChaoyangHe(String s, String t) { 3 //init a collection or int value to save the result according the question. 4 List<Integer> result = new LinkedList<>(); 5 if(t.length()> s.length()) return result; 6 7 //create a hashmap to save the Characters of the target substring. 8 //(K, V) = (Character, Frequence of the Characters) 9 Map<Character, Integer> map = new HashMap<>(); 10 for(char c : t.toCharArray()){ 11 map.put(c, map.getOrDefault(c, 0) + 1); 12 } 13 //maintain a counter to check whether match the target string. 14 int counter = map.size();//must be the map size, NOT the string size because the char may be duplicate. 15 16 //Two Pointers: begin - left pointer of the window; end - right pointer of the window 17 int begin = 0, end = 0; 18 19 //the length of the substring which match the target string. 20 int len = Integer.MAX_VALUE; 21 22 //loop at the begining of the source string 23 while(end < s.length()){ 24 25 char c = s.charAt(end);//get a character 26 27 if( map.containsKey(c) ){ 28 map.put(c, map.get(c)-1);// plus or minus one 29 if(map.get(c) == 0) counter--;//modify the counter according the requirement(different condition). 30 } 31 end++; 32 33 //increase begin pointer to make it invalid/valid again 34 while(counter == 0 /* counter condition. different question may have different condition */){ 35 36 char tempc = s.charAt(begin);//***be careful here: choose the char at begin pointer, NOT the end pointer 37 if(map.containsKey(tempc)){ 38 map.put(tempc, map.get(tempc) + 1);//plus or minus one 39 if(map.get(tempc) > 0) counter++;//modify the counter according the requirement(different condition). 40 } 41 42 /* save / update(min/max) the result if find a target*/ 43 // result collections or result int value 44 45 begin++; 46 } 47 } 48 return result; 49 } 50 }
这里是leetcode大神给出的通用模板,针对每个题目的不同会有一些差别。但是整体的思路就是两个指针,一个map,一个counter标记(视题目而定,也可能没有)。具体的思路就是先滑动窗口,找到一个窗口内包含一个结果;然后缩小窗口,判断结果。
类似的leetcode上题目及其解法如下:
Longest Substring Without Repeating Characters
Given a string, find the length of the longest substring without repeating characters.
Examples:
Given "abcabcbb"
, the answer is "abc"
, which the length is 3.
Given "bbbbb"
, the answer is "b"
, with the length of 1.
Given "pwwkew"
, the answer is "wke"
, with the length of 3. Note that the answer must be a substring, "pwke"
is a subsequence and not a substring.
分析:题目要求寻找一个字符串s中的最长的不重复字串的长度。使用昨天学习的滑动窗口算法可以很容易的找到这个题目的求解思路。
首先,滑动窗口包含一个结果,一个结果就是不重复的字串。
然后,在一个结果中移动左指针,实现窗口的扩充。
关键还是在于左右指针移动和map的含义。在这里,map是一个character——integer键值对,用来保存窗口内出现的字符的数量。right指针右移时,该位置的value+1。如果该位置的value>1,就说明在窗口内已经有了这个字符了,所以counter+1,并且进入步骤二。在步骤二里,首先判断left位置的字符是不是那个重复的字符,如果是,counter-1,并且将该字符对应的value-1;如果不是,直接将该位置的value-1,并且继续取找这个重复的字符。
代码如下:
1 class Solution { 2 public int lengthOfLongestSubstring(String s) { 3 Map<Character,Integer> map = new HashMap<>(); 4 int left = 0,right=0,counter=0; 5 int maxlength = 0; 6 while ( right < s.length() ){ 7 //实现窗口的扩大。counter标识是否c字符是否重复出现。 8 char c = s.charAt(right); 9 map.put(c,map.getOrDefault(c,0)+1); 10 if ( map.get(c) > 1 ) counter++; 11 right++; 12 //当窗口里包含一个结果时 13 while ( counter > 0 ){ //right指示的字符再窗口里由重复 14 char c_left = s.charAt(left); 15 if ( map.get(c_left) > 1 ) counter--; 16 map.put(c_left,map.get(c_left)-1); 17 left++; 18 } 19 maxlength = Math.max(maxlength,right-left); 20 } 21 return maxlength; 22 } 23 }
结果非常理想,竟然击败了100%的人,用时36ms,不得不说Sliding Window algorithm用O(n)的时间复杂度就可以解决substring searching的问题,非常非常有效。强烈建议理解这种方法。
Minimum Window Substring
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).
Example:
Input: S = "ADOBECODEBANC", T = "ABC" Output: "BANC"
Note:
- If there is no such window in S that covers all characters in T, return the empty string
""
. - If there is such window, you are guaranteed that there will always be only one unique minimum window in S.
分析:这个题目是要求找s中包含t的所有字符的最小的字符串。显然还是使用我们的滑动窗口法比较方便。思想和步骤还是那些,这里比较特色的是要求返回一个字符串,分解一下就是要找到开头字符和长度,然后用substring就可以了。之前我还想着用stringbuffer,略略智障。代码如下:
1 class Solution { 2 public String minWindow(String s, String t) { 3 if ( s.length() < t.length() ) return ""; 4 Map<Character,Integer> map = new HashMap<>(); 5 for ( char c : t.toCharArray() ) map.put(c,map.getOrDefault(c,0)+1); 6 int left=0,right=0; 7 int counter=map.size(); 8 int len = Integer.MAX_VALUE,head=0; 9 while ( right < s.length() ){ 10 //实现窗口的扩大,当counter==0的时候,窗口内包含一个结果。 11 char c = s.charAt(right); 12 if ( map.containsKey(c) ) { 13 map.put(c,map.get(c)-1); 14 if ( map.get(c) == 0 ) counter--; 15 } 16 right++; 17 18 while ( counter == 0 ){ 19 char c_left = s.charAt(left); 20 if ( map.containsKey(c_left) ) { 21 map.put(c_left,map.get(c_left)+1); 22 if ( map.get(c_left) > 0 ) counter++; 23 } 24 if ( right-left < len ){ 25 len = right-left; 26 head = left; 27 } 28 left++; 29 } 30 } 31 if ( len == Integer.MAX_VALUE ) return ""; 32 return s.substring(head,head+len); 33 } 34 }
运行时间22ms,还可以。看了一下他们7ms大神,基本都是用int[256]的数组来取代map。因为这里想直接保存字符类型数组,所以根据ascii码表,用的new int[256]。但是我回去查了一下,ascii码表只有128个,并且我将数组长度改成128之后也可以AC,那么为什么用256呢?不是很明白,先放在这疑问。