题目
Given a string which contains only lowercase letters, remove duplicate letters so that every letter appear once and only once. You must make sure your result is the smallest in lexicographical order among all possible results.
Example 1:
Input: "bcabc"
Output: "abc"
Example 2:
Input: "cbacdcbc"
Output: "acdb"
Difficulty: Hard
分析
题目的关键是,要给出词典顺序最小的结果字符串。
我一开始的想法是,从后往前检查,把搜到的第一个不唯一的字符 s[i]
,和它前面的一个副本 s[j]
,比较 s[j..i-1]
和 s[j+1...i]
,从而决定要选择哪个位置,去除哪个字符。
比如:
bcabc -> (remove b) -> babc -> (remove b) -> abc
cbacdcbc -> (remove c) -> cbacdbc -> (remove c) -> cbacdb -> (remove b) -> cacdb -> (remove c) -> acdb
但是有一个反例:
defcbeabdc -> (remove c) -> defbeabdc -> (remove d) -> defbeabc
-> (remove b) -> defbeac -> (remove e) -> defbac
正确答案应为 defabc
。
贪心算法
在网站上我看到了这个递归的贪心算法,它的思路是这样的。
思路
每一次的贪心选择即为答案最左端的字母。如何进行贪心选择呢?由题目要求可以知道,这个贪心选择得到的字母应该尽可能地小。姑且称呼这个选择为“最小字母”,它应当是在一定范围内最小的字母。
但是,这样的贪心选择必然有中止条件或限制范围。假想某个“最小字母”左边有唯一的字母,比如CBDDF,B左边有C,C在该字符串中是唯一的。这种情况下,以B作为贪心选择是不合理的。
而如果“最小字母”左边的字母,在“最小字母”右边也有它们的重复,那么左边的这些字母完全可以舍去,它们不会比“最小字母”小,由它们无法得到字典顺序最小的答案字符串。比如CBABC,A左边的CB可以舍去。
所以,字符串 s
中的 “最小字母” s[pos]
应该满足这样的条件:
-
s[0...pos-1]
中的字母,在字符串s
中不是唯一的 -
s[pos...]
中的字母包括了所有在字符串s
中唯一的字母
(在字符串 s
中没有唯一字母的情况下,会有多个“最小字母”,比如abcacb。这时选择最左边的“最小字母”。)
取出 s[pos]
,加入答案字符串中。把右边的子串 s[pos+1...]
中重复的字母 s[pos]
去掉后,对新的字符串重复上述操作。
例子
答案字符串 | 待处理字符串 | 第一个唯一字母 | 最小字母 |
---|---|---|---|
defcbeabdc | f | d | |
d | efcbeabc | f | e |
de | fcbabc | f | f |
def | cbabc | a | a |
defa | bc |