https://leetcode-cn.com/problems/remove-duplicate-letters/ß

    Tip:涉及的题目如果全是字母,可以往使用26个数组的方向考虑。 Tip:针对不允许修改顺序这一类说法,可以往栈靠。

    1. class Solution {
    2. public String removeDuplicateLetters(String s) {
    3. // 时间复杂度:O(N)
    4. // 空间复杂度:O(N)
    5. // 递归的基准情形
    6. if (s.length() == 0) return "";
    7. // 希望找到最左侧位置,位置记为 position
    8. int position = 0;
    9. int[] countArray = new int[26];
    10. for (int i = 0; i < s.length(); i++) {
    11. // 计算出字母的次数
    12. countArray[s.charAt(i) - 'a']++;
    13. }
    14. // 遍历字符串,找到可以放在最左边的字母
    15. for (int i = 0; i < s.length(); i++) {
    16. // 把当前字符和 position 位置比较,如果更小就替换
    17. // 这里每次遍历都是尝试当前字符能否删,能删则他的 count 值 -1,
    18. // 且如果当前字符小于 position,则需要替换:因为第一轮比较的是自己,如果后面退出成立,那 OK 没问题。
    19. // 如果不成立,则说明这个字符可以删,所以遇到更小的要立即替换
    20. // 不能删,就推出了。position 停留的位置就是最后剩的初始字符
    21. if (s.charAt(i) < s.charAt(position)) {
    22. position = i;
    23. }
    24. // 每遇到一个字母,其 count--
    25. // 如果此时 count 值为 0,以当前字母作为最左端字符
    26. if (--countArray[s.charAt(i) - 'a'] == 0) {
    27. break;
    28. }
    29. }
    30. // 递归调用
    31. return s.charAt(position) +
    32. removeDuplicateLetters(s.substring(position + 1).
    33. replaceAll(s.charAt(position) + "", ""));
    34. }
    35. }
    1. class Solution {
    2. public String removeDuplicateLetters(String s) {
    3. // 时间复杂度:O(N)
    4. // 空间复杂度:O(1)
    5. Stack<Character> resStack = new Stack<>();
    6. // 快速判断一个字符是否在结果中出现过,使用 HashSet 保存元素是否出现
    7. HashSet<Character> seenSet = new HashSet<>();
    8. // 为了快速判断一个字符是否在某个位置之后出现,用一个 HashMap 保存字母出现在字符串中出现的位置
    9. HashMap<Character, Integer> lastOccur = new HashMap<>();
    10. // 遍历字符串,将最后一个出现的位置保存进 lastOccur
    11. for (int i = 0; i < s.length(); i++) {
    12. // map 会覆盖重复出现的 key
    13. lastOccur.put(s.charAt(i), i);
    14. }
    15. // 遍历字符串,判断每个字符是否需要入栈
    16. for (int i = 0; i < s.length(); i++) {
    17. char curChar = s.charAt(i);
    18. // 只有在 curChar 没有出现过的情况下,才判断是否入栈
    19. if (!seenSet.contains(curChar)) {
    20. // curChar 入栈操作之前,判断之前的栈顶元素是否在后面重复出现,如果是,需要弹出
    21. while (!resStack.isEmpty() && curChar < resStack.peek() && lastOccur.get(resStack.peek()) > i) {
    22. // 此外,弹出之后,seenSet 就等于没有出现,需要删除
    23. seenSet.remove(resStack.pop());
    24. }
    25. // 真正入栈操作
    26. resStack.push(curChar);
    27. seenSet.add(curChar);
    28. }
    29. }
    30. // 将 resStack 元素保存成字符串输出
    31. StringBuilder stringBuilder = new StringBuilder();
    32. for (Character character : resStack) {
    33. stringBuilder.append(character.charValue());
    34. }
    35. return stringBuilder.toString();
    36. }
    37. }

    image.png