LeetCode316/1081 Remove Duplicate Letters/Smallest Subsequence of Distinct Characters
LC316 and LC1081 are essentially the same.
Given a string which contains only lowercase letters, remove duplicate letters so that every letter appears once and only once. You must make sure your result is the smallest in lexicographical order among all possible results.
example:
Input: “cbacdcbc”
Output: “acdb”
this is a easy understanding problem:
like many other easy understanding problems, I don’t have any thoughts about this.
but this time, instead of just looking for the solution right away, let’s thinking about this: the problem requires us to return the smallest in lexicographical order among all potential results.
since “a” is smallest, let’s check if a is duplicate:
if a is, then we choose the a that is ahead most. and then we look at b, and we choose b that is ahead most among all "b"s and then… until the successfully find all chars. or we can’t find them all.
(but I;m kind of confused: if each time we choose the ahead most isn’t gonna work, how come we choose later will gonna work?)
well, the answer is, we are not asked to return all chars in the order of abcde… instead, if abcde is not gonna work, but bcdea might work, and that did count as the results.
so are we gonna use the “try and fail until succeed” way, or is there another better way we solve this?
Keywords to solve this problem: Greedy, stack
//a recursion way, not fully understand, but it worked out just fine.
public class Solution {
public String removeDuplicateLetters(String s) { //this function will return the smallest and nonduplicate string
int[] cnt = new int[26];
int pos = 0; // the position for the smallest s[i]
for (int i = 0; i < s.length(); i++) cnt[s.charAt(i) - 'a']++;
for (int i = 0; i < s.length(); i++) {
//pos will stopped at the smallest element in current s
if (s.charAt(i) < s.charAt(pos)) pos = i;
//update every i it passes by one each step
cnt[s.charAt(i) - 'a']--;
//when current char left nothing, then we break, and at this time, the pos point to the char we need to add the part of our final solution
if (cnt[s.charAt(i) - 'a'] == 0) break;
}
return s.length() == 0 ? "" : s.charAt(pos) + removeDuplicateLetters(s.substring(pos + 1).replaceAll(""+s.charAt(pos), "")); //tail recursion, and we need to remove a.charAt(pos) in remaining substring
}
}
//the following is stack-based solution: we maintain a increasing stack
and the general idea is:
Find the index of last occurrence for each character.
Use a stack to keep the characters for result.
Loop on each character in the input string S,
if the current character is smaller than the last character in the stack, and the last character exists in the remaining stream, we can pop the last character to get a smaller result. //this is the most important idea of the whole solution.
how can we make sure the last char in the stack exist in the remaining streaming? we use int[] last = new int[26], seen = new int[26]; last use to check if current index reached the last index current char appears. and seen use in check a char in current stack(because stack is the result we are gonna return, so anytime it shouldn’t contains any duplicate chars)
//in following code, stack stores the Intergers of each chars
public String smallestSubsequence(String S) {
Stack<Integer> stack = new Stack<>();
int[] last = new int[26], seen = new int[26];
for (int i = 0; i < S.length(); ++i)
last[S.charAt(i) - 'a'] = i;
for (int i = 0; i < S.length(); ++i) {
int c = S.charAt(i) - 'a';
if (seen[c]++ > 0) continue;
while (!stack.isEmpty() && stack.peek() > c && i < last[stack.peek()])
seen[stack.pop()] = 0;
stack.push(c);
}
StringBuilder sb = new StringBuilder();
for (int i : stack) sb.append((char)('a' + i));
return sb.toString();
}
//the following code, stack stores the chars
public class Solution {
public String removeDuplicateLetters(String s) {
Stack<Character> stack = new Stack<>();
int[] count = new int[26];
char[] arr = s.toCharArray();
//build the hashmap
for(char c : arr) {
count[c-'a']++;
}
boolean[] visited = new boolean[26];
for(char c : arr) { //for each char
count[c-'a']--;
if(visited[c-'a']) { //if this is visited
continue;
}
//stack will only maintains increasing chars sequences
while(!stack.isEmpty() && stack.peek() > c && count[stack.peek()-'a'] > 0) { //so when we meet a decrease char and char at the peek() of stack
visited[stack.peek()-'a'] = false; //then we mark this as not visited
stack.pop();//and pop it
}
stack.push(c); //we push current char
visited[c-'a'] = true; //and mark current char as visited
}
StringBuilder sb = new StringBuilder();
for(char c : stack) { //can we just interate stack?
sb.append(c);
}
return sb.toString();
}
}

浙公网安备 33010602011771号