Given a string s
, remove duplicate letters so that every letter appears once and only once. You must make sure your result is the smallest in lexicographical order among all possible results.
Example 1:
Input: s = "bcabc" Output: "abc"
Example 2:
Input: s = "cbacdcbc" Output: "acdb"
Constraints:
1 <= s.length <= 104
s
consists of lowercase English letters.
题目给定一个字符串,要求删掉字符串里重复的字符使得最后字符串里的每个字符都只出现一次。 另外删除字符的过程中要尽可能使最后的字符串按字典序排列最小。
我们知道一串字符要是所有字符是按从小到大排列好的那么它就是字典序最小的那个字符串,因此这题就要通过删除字符尽可能地使字符按递增排列。这也就很容易使我们想起用单调递增栈来解答。要尽可能地把小的字符留在堆栈里,尽可能地把在前面但比较大的字符删除。现在来分析一下当遇到一个字符后在什么情况下可以把栈里的字符弹出,在什么情况下该字符可以入栈。
1)要是字符在堆栈里就已经存在了,那说明这个字符在前面已经被保留用来使得字符串更小,因此当前遇到该字符就不需要再处理直接丢弃。这里可以用一个set来记录字符是否已经存在堆栈里。
2)要是字符在堆栈里不存在,在入栈前要先判断栈里有没有比当前字符大并且可以删掉的字符。那么栈里比当前字符大的字符在什么情况下可以删掉呢?本着大字符应尽可能排在后面的原则,如果在当前字符之后还有跟栈里一样的字符,那么在栈里的就可以弹出删掉(弹出后在set里也要同时删掉)。为了要快速判断当前字符之后是否还有跟栈里一样的字符,可以事先用一个字典来记录每个字符在数组中最后出现的位置,如果字符最后出现的位置是在当前字符位置的右侧就说明当前字符之后还存在该字符。把栈里所有能弹出的字符都弹出,当前字符就可以入栈了(并加入到set里)。
最后栈里剩下的所有字符都将是唯一的,并且从栈头到栈顶的字符顺序就是字典序最小的字符串。
class Solution:
def removeDuplicateLetters(self, s: str) -> str:
st = []
rightMost = {}
inStack = set()
for i in range(len(s)):
rightMost[s[i]] = i
for i in range(len(s)):
if s[i] not in inStack:
while st and st[-1] > s[i] and i < rightMost[st[-1]]:
inStack.discard(st.pop())
st.append(s[i])
inStack.add(s[i])
return ''.join(st)