题目
给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。
题目分析
这个问题一共有2部分,第一个是去除重复的元素,第二个是字典序最小。
对于第一个问题很好理解,就是输入的字符串包含重复的字符,需要我们去重,没啥好说的。
第二个问题:为什么会存在字典序最小这个问题?因为给定的字符串中的英文字母是乱序的,没有保持从小到大的顺序,(即:a < b < c < d < ... < z
)。如果是保持从小到大的顺序,不管怎么重复,都很好解决。例如aabbbbcccdddzzzz
让你删除重复的元素并保持字典序最小,就非常简单了。
因此我们知道了输入字符串的乱序是造成字典序最小
这个问题的原因之一。
此外单个字符串的字典序是确定的,没有所谓的大小。但是如果要求我们把重复的字符删掉,同时保证其余的字符顺序保持不变
。这样子原来输入的字符串就能够演变成多个字符串,因此多个乱序字符串之间就可以比较字典序大小了。
综上,乱序
和去重
是导致字典序最小
问题的原因。
因此如果要使得字典序最小,就要关注造成乱序
的那个字符。举个例子,对于cbacdcbc
:
-
第一个字符是
c
; -
第二个字符是
b
,b
比c
小,形成了乱序,那么如果我们能够把b
前面的c
删掉,这个乱序就消失了。
2.1. 那么问题来了:能不能删呢?答案是可以的,因为题目本来就让我们去重,而想要删掉的b
,在这个字符串后面还出现了2次,那当然可以删除。 -
第三个字符是
a
,a
比b
小,又形成了乱序,同样的道理,如果我们能把b
删掉,就解决了这个乱序。能不能删?可以!因为后面还有一个b, cbacdcbc。 -
经过上面的操作,已经暂时确定字符是
a
,c
和b
都被我们删了 -
第四个字符是
c
,满足字典序,暂时确定的字符是ac
-
第五个字符是
d
,满足字典序,暂时确定的字符就是acd
-
第六个字符是
c
,注意:这个字符在我们暂时确定的字符acd
中已经出现了,但是题目要求我们只能保留一个,我们究竟是删除前面的字符c
,还是删除我们现在遍历到的字符c
?,答案很明显,应该删除现在遍历到字符c
,因为我们暂时确定的字符acd
是满足字典序的,如果删除前面一个c
,保留遍历到的c
,就变成了adc
,显然,字典序变大了。
7.1. 值得注意的是,如果第六个字符是d
,其实情况有点不同:已经暂时确定的字符是acd
,现在遇到了d
,这个时候删除哪个d
都是一样的。所以为了统一,统一理解为删除当前遍历到的字符 -
第7个字符是
b
,已经确定的字符是acd
,同样不满足字典序了,但是能将d
字符删掉么?答案是不不能的,因为题目要求每个字符必须保留一个,后面已经没有d
了,怎么能删呢?所以虽然这里不满足字典序但是不能删除,因此已经确定的字符是acdb
-
第8个字符是c,这个字符在已经确定的字符
acdb
中已经出现过了,按照上面的说法,应该删除掉当前遍历到的字符。
10.所以最后的结果就是acdb
代码流程
经过上面的例子分析,我们总结得到如下的处理过程:
- 情况1:遍历这个字符串,如果遇到了满足字典序的情况,例如
abcd
,就直接按照顺序存下来 - 如果遇到了不满足字典序的情况:例如
abcd
后面一个是c
。这个时候得处理乱序,就得分情况考虑,为了方便起见,我们将与遍历到的字符形成乱序的字符,叫做关键字符
,就是上例的d
:- 情况2:如果新遇到的字符在已经暂时确定的字符串中出现过,处理乱序的做法是:删除的是当前遍历到的字符,但具体做法是跳过这个字符,不将他添加到结果序列中,等价于删除了
- 情况3:如果新遇到的字符没有在暂时确定的字符串中出现过,并且
关键字符
在后面不再出现了,这是处理乱序的做法是:接受这个乱序,将遍历到的字符保存下来,因为不能删除元素 - 情况4:如果新遇到的字符没有在暂时确定的字符串中出现过,并且
关键字符
在后面还会出现,这时候就可以将关键字符
删掉
- 以上一共4种情况,前3种都可以遍历下一个元素,但是最后一种情况不行,因为他将
关键字符
删除了,需要继续判断当前遍历的元素与新的关键字符
的关系,重复上述过程。举个例子adf
遇到了c
,假设后面还有df
,因此根据上述说法,f
将会被删掉,但是c
还是与新的关键字符
形成了乱序
,又得循环判断一次。
因此,我们可以将暂时确定的字符
保存在栈中,关键字符
的删除,等价于栈的弹出,然后新的栈顶元素充当关键字符
。我们需要判断某个字符在后序还会不会出现,因此需要实时记录遍历到当前位置,后面字符剩余个数的情况,可以用一个int[] nums
数组来记录。还需要判断当前遍历到的字符,有没有在暂时确定的字符
中出现过,即有没有在栈中出现,因此需要一个boolen[] isInStack
数组来记录存在情况。
代码实现
public class Solution01 {
public String removeDuplicateLetters(String s) {
if (s == null