import java.util.*;
/**
* NC375 去除重复字母
* @author d3y1
*/
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param str string字符串
* @return string字符串
*/
public String removeDuplicateLetters (String str) {
// return solution1(str);
// return solution2(str);
return solution3(str);
}
/**
* 贪心+单调栈
* @param str
* @return
*/
private String solution2(String str){
int n = str.length();
HashMap<Character, Integer> lastIndexMap = new HashMap<>();
char cur;
for(int i=n-1; i>=0; i--){
cur = str.charAt(i);
if(!lastIndexMap.containsKey(cur)){
lastIndexMap.put(cur, i);
}
}
Deque<Integer> deque = new LinkedList<>();
HashSet<Character> usedSet = new HashSet<>();
// 贪心
for(int i=0; i<n; i++){
cur = str.charAt(i);
// 测试用例: ambcadcb -> ambcd
// 测试用例: mabcadcb -> mabcd
if(usedSet.contains(cur)){
continue;
}
// 单调栈(单调增)
// lastIndexMap.get(str.charAt(deque.peekLast()))>i -> 表示栈顶字符在右侧(后面)有重复
while(!deque.isEmpty() && str.charAt(deque.peekLast())>cur && lastIndexMap.get(str.charAt(deque.peekLast()))>i){
usedSet.remove(str.charAt(deque.pollLast()));
}
deque.offerLast(i);
usedSet.add(cur);
}
StringBuilder result = new StringBuilder();
while(!deque.isEmpty()){
result.append(str.charAt(deque.pollFirst()));
}
return result.toString();
}
/**
* 贪心+单调栈
* @param str
* @return
*/
private String solution3(String str){
int n = str.length();
char[] letters = str.toCharArray();
int[] lastIndex = new int[26];
// 当前字符 最后索引位置
for(int i=0; i<n; i++){
lastIndex[letters[i]-'a'] = i;
}
boolean[] isUsed = new boolean[26];
Deque<Character> deque = new ArrayDeque<>();
char cur;
// 贪心
for(int i=0; i<n; i++){
cur = letters[i];
if(isUsed[cur-'a']){
continue;
}
// 单调栈(单调增)
// lastIndex[deque.peekLast()-'a']>i -> 表示栈顶字符在右侧(后面)有重复
while(!deque.isEmpty() && deque.peekLast()>cur && lastIndex[deque.peekLast()-'a']>i){
isUsed[deque.pollLast()-'a'] = false;
}
deque.offerLast(cur);
isUsed[cur-'a'] = true;
}
StringBuilder result = new StringBuilder();
while(!deque.isEmpty()){
result.append(deque.pollFirst());
}
return result.toString();
}
//////////////////////////////////////////////////////////////////////////////////////
/**
* 贪心+单调栈
* @param str
* @return
*/
private String solution1(String str){
Stack<Character> stack = new Stack<>();
boolean[] isVisited = new boolean[26];
int[] lastIndex = new int[26];
char[] chars = str.toCharArray();
int len = chars.length;
for(int i=0; i<len; i++){
lastIndex[chars[i]-'a'] = i;
}
// 贪心
for(int i=0; i<len; i++){
if(isVisited[chars[i]-'a']){
continue;
}
// 单调栈(单调增)
// lastIndex[stack.peek()-'a']>i -> 表示栈顶字符在右侧(后面)有重复
while(!stack.isEmpty() && stack.peek()>chars[i] && lastIndex[stack.peek()-'a']>i){
isVisited[stack.pop()-'a'] = false;
}
stack.push(chars[i]);
isVisited[chars[i]-'a'] = true;
}
StringBuilder sb = new StringBuilder();
while(!stack.isEmpty()){
sb.append(stack.pop());
}
return sb.reverse().toString();
}
}