Given a string, find the length of the longest substring T that contains at most 2 distinct characters.
For example, Given s = “eceba”
,
T is "ece"
which it length is 3.
The linear solution to this problem is to maintain a sliding window with a substring that contains up to two different characters. When you want to add a new character, you need to completely remove all occurrences of one of the previous characters. Here are two questions to consider:
1) in the existing two characters, how to choose to remove all occurrences of its characters?
2) How do I change the window after I have selected the removed characters?
for question 1, the character at the beginning of the window must be removed, but should you always select the character and then remove all occurrences? Taking "Abac" as an example, it can be found that when scanning to C, a is bound to be removed, but if you remove all occurrences of a, then there is only "C" left. This should be the removal of all occurrences of B, by removing the first of a, so as to get "AC." From this point of view, the selection criteria should be the last occurrence of the character position, the last occurrence of the position of the left (early), then its appearance is deleted after the decrease in the length of the less . Therefore, you should delete the last occurrence position at the leftmost character.
For question 2, because the topic stipulates that the window will have a maximum of only 2 characters, in fact, how to delete can be: the slow can be deleted from left to right, fast, you can directly let the new window to the last occurrence of the selected character position of the next character.
The following code uses a map structure to represent sliding window,key as a character, and value for the corresponding last occurrence position. In fact, you can completely avoid the use of map, because the inside of the entry up to only two, a waste of space. This is due to the subsequent extension and maintenance of the code.
In order to get the earliest characters in the last position of the map, we traverse all the entry. This is actually very inefficient, but considering that there are only 2 elements in the map, the overhead of traversal is small and negligible.
public int lengthoflongestsubstringtwodistinct (String s) {int start = 0;int MaxLen = 0;//key:letter; value:the Index of The last occurrence. Map<character, integer> map = new Hashmap<character, integer> (), int i;for (i = 0; i < s.length (); ++i) {Cha R C = S.charat (i); if (map.size () = = 2 &&!map.containskey (c)) {//Pick the character with the leftmost last Occurr Ence.char charendsmostleft = '; int minlast = S.length (); for (char Ch:map.keySet ()) {int last = map.get (ch), if (last &L T Minlast) {minlast = Last;charendsmostleft = ch;}} Map.Remove (charendsmostleft); start = Minlast + 1;} Map.put (c, i); maxlen = Math.max (maxlen, I-start + 1);} return maxlen;}
Topic extension, what if you allow the oldest string to contain k different characters? Note that the K here can be very large, so the inner loop overhead of the above code becomes O (K) instead O (1), and the entire complexity becomes O (n*k). Therefore, we need to avoid linear lookups within the map. In addition, because the array subscript is in the location of value in map, it cannot be sorted using heap or treemap.
At this point you can change the idea, in question 1) and the solution of question 2 to do a little compromise: for problem 1) directly from the left to the right character by the deletion, has been deleted to a character will not appear again. For question 2) because it is deleted from left to right, the resizing window chooses a slower method and does not skip any characters, but the time complexity of adjusting the window does not change.
To remove from left to right, you need to change the contents of the value stored in the map to the number of occurrences of the character in the window . In this way, the number of times a character is deleted is reduced to 0.
public int lengthoflongestsubstringkdistinct (String s, int k) {int start = 0;int MaxLen = 0;//key:letter; value:the num ber of occurrences. Map<character, integer> map = new Hashmap<character, integer> (), int i;for (i = 0; i < s.length (); ++i) {Cha R C = S.charat (i), if (Map.containskey (c)) {Map.put (c, Map.get (c) + 1),} else {map.put (c, 1); while (Map.size () > k) {Cha R Startchar = S.charat (start++), int count = Map.get (Startchar), if (Count > 1) {map.put (Startchar, count-1);} else {ma P.remove (Startchar);}}} MaxLen = Math.max (maxlen, I-start + 1);} return maxlen;}
Note that each of these elements will enter the window once, up to the window once, so the time complexity of the split on each element is O (1), the total time complexity is O (N), so when K is larger, it will be more efficient than the previous one.
[Leetcode] Longest Substring with at the most of the Distinct characters and extension