给定一个字符串 s ,请你去除字符串中重复的字母(剩下的字符串中每个字符仅出现一次),并在不改变其相对位置的情况下使剩下的字符串字典序最小。
数据范围:字符串长度满足
, 字符串中仅出现小写英文字母
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(); } }
import java.util.*; public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param str string字符串 * @return string字符串 */ public String removeDuplicateLetters (String str) { // write code here int [] cnt = new int [26]; for (int i = 0; i < str.length(); i++) { cnt[str.charAt(i) - 'a']++; } int pos = 0; for (int i = 0; i < str.length(); i++) { if (str.charAt(pos) > str.charAt(i)) { pos = i; } if (--cnt[str.charAt(i) - 'a'] == 0) { break; } } return str.length() == 0 ? "" : str.charAt(pos) + removeDuplicateLetters(str.substring(pos + 1).replaceAll("" + str.charAt(pos), "")); } }