import java.util.HashMap; import java.util.HashSet; import java.util.Stack; public class RemoveDuplicateLetters { // 方法一:暴力法,贪心策略递归 public String removeDuplicateLetters1(String s){ // 递归的基准情形 if ( s.length() == 0 ) return ""; // 希望找到当前最左侧的字母,位置记为position int position = 0; // 遍历字符串 for (int i = 0; i < s.length(); i++){ // 只有当前字母比已经找到的position位置的字母要小,才有资格继续判断 if (s.charAt(i) < s.charAt(position)){ // 定义一个布尔变量,表示当前i位置的字母是否可以替换position位置的字母 boolean isReplaceable = true; // 遍历i之前的所有字母,判断是否在i后面重复出现 for (int j = position; j < i; j++){ // 定义一个布尔变量,表示j位置的字母是否重复出现 boolean isDuplicated = false; // 遍历i后面的所有字母,看j位置的字母是否重复出现 for (int k = i + 1; k < s.length(); k++){ if (s.charAt(k) == s.charAt(j)){ isDuplicated = true; break; } } // 如果任一字母不重复出现,就不能替换当前position,后面的字母不用判断 if (!isDuplicated){ isReplaceable = false; break; } } if (isReplaceable) position = i; } } // 遍历结束,position位置的字母就是结果中最左侧的元素 return s.charAt(position) + removeDuplicateLetters1(s.substring(position+1).replaceAll(s.charAt(position) + "", "")); } // 方法二:贪心策略改进 public String removeDuplicateLetters2(String s){ // 递归的基准情形 if ( s.length() == 0 ) return ""; // 希望找到当前最左侧的字母,位置记为position int position = 0; // 定义一个count数组,保存所有26个字母在字符串中出现的频次 int[] count = new int[26]; for (int i = 0; i < s.length(); i++) count[s.charAt(i) - 'a'] ++; // count[0]保存a的个数;count[1]保存b的个数 // 遍历字符串,找到当前最左端字母 for (int i = 0; i < s.length(); i++){ // 把当前字符和position位置比较,如果更小就替换 if (s.charAt(i) < s.charAt(position)) position = i; // 每遇到一个字符,count值就要减1 // 如果遇到count减为0,就直接退出,以当前最小的字母作为最左端字符 if (--count[s.charAt(i) - 'a'] == 0) break; } // 递归调用 return s.charAt(position) + removeDuplicateLetters2(s.substring(position+1).replaceAll(s.charAt(position) + "", "")); } // 方法三:使用栈进行优化 public String removeDuplicateLetters(String s){ // 定义一个字符栈,保存去重之后的结果 Stack<Character> stack = new Stack<>(); // 为了快速判断一个字符是否在栈中出现过,用一个Set来保存元素是否出现 HashSet<Character> seenSet = new HashSet<>(); // 为了快速判断一个字符是否在某个位置之后重复出现,用一个HashMap来保存字母出现在字符串的最后位置 HashMap<Character, Integer> lastOccur = new HashMap<>(); // 遍历字符串,将最后一次出现的位置保存进map for (int i = 0; i < s.length(); i++){ lastOccur.put(s.charAt(i), i); } // 遍历字符串,判断每个字符是否需要入栈 for (int i = 0; i < s.length(); i++){ char c = s.charAt(i); // 只有在c没有出现过的情况下,才判断是否入栈 if (!seenSet.contains(c)){ // c入栈之前,要判断之前栈顶元素,是否在后面会重复出现;如果重复出现就可以删除 while ( !stack.isEmpty() && c < stack.peek() && lastOccur.get(stack.peek()) > i ){ seenSet.remove(stack.pop()); } stack.push(c); seenSet.add(c); } } // 将栈中的元素保存成字符串输出 StringBuilder stringBuilder = new StringBuilder(); for (Character c: stack) stringBuilder.append(c.charValue()); return stringBuilder.toString(); } public static void main(String[] args) { String str1 = "bcabc"; String str2 = "cbacdcbc"; RemoveDuplicateLetters removeDuplicateLetters = new RemoveDuplicateLetters(); System.out.println(removeDuplicateLetters.removeDuplicateLetters(str1)); System.out.println(removeDuplicateLetters.removeDuplicateLetters(str2)); } }