LeetCode 去除重复字母(316题)

LeetCode 去除重复字母(316题)

@author:Jingdai
@date:2020.10.27

题目描述(316题)

给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。

  • 示例输入
s = "bcabc"
  • 示例输出
"abc"
  • 提示
s 由小写英文字母组成

思路

题目大概一看可能会认为很简单,不就是去个重嘛。但仔细看会发现不是这样,题目的难点根本不是去重,而是要使字典序最小,同时不能打乱其他字符的位置。

使字典序最小,这个就类似于 LeetCode 的移掉K位数字(402题),也是利用栈,在 push 入一个新字母前,看前面的字母是否比它大,如果大的话就 pop 出前面的字母,直到前面的字母比它小为止,然后 push 入当前字母。

这样可以保证是字典序最小了,但是又有一个新的问题,就是可能 pop 出的字母只有一个,这样就会使最后的字符串没有了这个字母,所以不能直接 pop。那应该怎么做呢?应该在 pop 前检查之后还有没有同样的字母,如果有的话再 pop ,这样可以保证最后不会缺少某个字母。这样就既保证最小序,又可以保证不会缺少某个字母。

那问题又来了,那怎么知道后面还有没有相同的字母呢?这个就很简单了,由于题目提示明确说明了 s 由小写字母构成,可以用一个长度为26的数组记录字母的个数,每遍历到一个字母就使该字母个数减一,如果某字母对应数组中的数字为0了,说明后面没有了,就不能 pop 了,否则说明后面还有,可以安全的 pop

哦,最后忘了最简单的去重,这个很好处理,直接用一个数组记录是否在栈中即可,当字母已经在栈中,则直接跳过该字母就行。代码如下。

代码

public String removeDuplicateLetters(String s) {
     
     

    int[] letterArray = new int[26];

    for (int i = 0; i < s.length(); i++) {
     
     
        letterArray[s.charAt(i) - 'a']++;
    }

    boolean[] isInStack = new boolean[26];
    LinkedList<Character> stack = new LinkedList<>();

    for (int i = 0; i < s.length(); i++) {
     
     
        char tempChar = s.charAt(i);
        letterArray[tempChar - 'a']--;
        if (isInStack[tempChar - 'a']) {
     
     
            continue;
        }
        while (stack.size() != 0 && tempChar < stack.peek()) {
     
     
            if (letterArray[stack.peek() - 'a'] > 0 ) {
     
     
                isInStack[stack.pop() - 'a'] = false;
            } else {
     
     
                break;
            }
        }
        isInStack[tempChar - 'a'] = true;
        stack.push(tempChar);
    }

    StringBuilder ans = new StringBuilder();
    while (stack.size() != 0) {
     
     
        ans.append(stack.removeLast());
    }
    return ans.toString();
}

猜你喜欢

转载自blog.csdn.net/qq_41512783/article/details/109306857