[LeetCode]Remove Duplicate Letters

题目描述:

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:

Given "bcabc"
Return "abc"

Given "cbacdcbc"
Return "acdb"

题目大意:

给定一个只包含小写字母的字符串,从中移除重复字母使得每个字母只出现一次。你必须确保结果的字典序最小。

测试用例如题目描述。

解题思路:

贪心算法(Greedy Algorithm)

时间复杂度 O(k * n),其中k为字符串中唯一字符的个数,n为字符串的长度

枚举字符串前缀,直到遇到首个唯一字符为止,从前缀中挑选出最小的字符作为首字符。

然后从剩余字符串中移除所有与首字母相同的字母。

重复此过程,直到选出所有唯一字符为止。

Python代码:

class Solution(object):
    def removeDuplicateLetters(self, s):
        """
        :type s: str
        :rtype: str
        """
        ans = ''
        for x in range(len(set(s))):
            top, idx = s[0], 0
            counter = collections.Counter(s)
            for y in range(len(s)):
                if top > s[y]:
                    top, idx = s[y], y
                if counter[s[y]] == 1:
                    break
                counter[s[y]] -= 1
            ans += top
            s = s[idx+1:].replace(top,'')
        return ans

算法的优化:

使用栈(Stack)数据结构对上述算法进行优化,可以使时间复杂度缩减为O(n)

算法步骤:

首先计算字符串s中每一个字符出现的次数,得到字典counter

遍历字符串s,记当前字符为c,将counter[c] - 1

如果c已经在栈stack中,继续遍历

将字符c与栈顶元素top进行比较,若top > c并且counter[top] > 0则弹栈,重复此过程

将c入栈

算法执行过程中,栈内元素尽可能的保持递增顺序

最后,栈中剩余元素即为所求字符串

Python代码:

class Solution(object):
    def removeDuplicateLetters(self, s):
        """
        :type s: str
        :rtype: str
        """
        counter = collections.Counter(s)
        resultSet = set()
        stack = list()
        for c in s:
            counter[c] -= 1
            if c in resultSet:
                continue
            while stack and stack[-1] > c and counter[stack[-1]]:
                resultSet.remove(stack.pop())
            resultSet.add(c)
            stack.append(c)
        return ''.join(stack)

另外附一个递归解法,参考:https://leetcode.com/discuss/74121/some-python-solutions

Python代码:

def removeDuplicateLetters(self, s):
    for c in sorted(set(s)):
        suffix = s[s.index(c):]
        if set(suffix) == set(s):
            return c + self.removeDuplicateLetters(suffix.replace(c, ''))
    return ''

 

本文链接:http://bookshadow.com/weblog/2015/12/09/leetcode-remove-duplicate-letters/
请尊重作者的劳动成果,转载请注明出处!书影博客保留对文章的所有权利。

如果您喜欢这篇博文,欢迎您捐赠书影博客: ,查看支付宝二维码

Pingbacks已关闭。

评论
  1. miao miao 发布于 2015年12月31日 13:09 #

    hi, how do you know the result is 字典序最小? in 贪心算法, you have to decide whether the pick the first, second, or other ones.

  2. 在线疯狂 在线疯狂 发布于 2015年12月31日 17:41 #

    这里表达的不是特别准确,应该是“最小的字符”。

  3. miao miao 发布于 2016年1月17日 15:19 #

    Thanks for your reply.

    good implementation of the greedy algorithm, though it took me a while to understand it (I don't use python). This sentence confused me a bit "枚举字符串前缀,直到遇到首个唯一字符为止". "唯一" isn't accurate. It could be the last appearance of a non-unique character.

    Nevertheless, I don't feel this could be implemented with greedy algorithm. For instance, "bcfabdefgc" -> "abdefgc", and the correct one should be "cabdefg". In fact, I feel this problem could only be solved by brute force, i.e. list all possible solutions and compare them and the get the smallest. Your thoughts?

  4. miao miao 发布于 2016年1月18日 09:36 #

    原来对字典序最小理解错了。几个实现都很好。

  5. 在线疯狂 在线疯狂 发布于 2016年1月18日 19:28 #

    是的,这里的“唯一”是指当前字符在剩余字符串中不再出现。另外,测试用例"bcfabdefgc“的正确结果的确是"abdefgc","cabdefg"的字典序不是最小的。

  6. 忍释 忍释 发布于 2017年2月20日 20:54 #

    stack运用的很棒

张贴您的评论