leetcode 剑指 Offer 38. 字符串的排列

剑指 Offer 38. 字符串的排列
输入一个字符串,打印出该字符串中字符的所有排列。

你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。

示例:

输入:s = “abc”
输出:[“abc”,“acb”,“bac”,“bca”,“cab”,“cba”]

限制:

1 <= s 的长度 <= 8


开始使用回溯,但是如果不适用集合就会超时

class Solution:
    def permutation(self, s: str) -> List[str]:
        if not s: return [""]
        out = set()#加入的集合,过滤重复的字符串
        def backtrack(s,  res):
            if not s:
                out.add(res)
            for i in range(len(s)):
                backtrack(s[:i]+s[i+1:], res + s[i])

        backtrack(s, "")
        # out = out.sort()
        return list(out)

重复方案与剪枝: 当字符串存在重复字符时,排列方案中也存在重复方案。为排除重复方案,需在固定某位字符时,保证 “每种字符只在此位固定一次” ,即遇到重复字符时不交换,直接跳过。从 DFS 角度看,此操作称为 “剪枝” 。

但是上面的方法运行的很慢,还有一个问题就是重复的字符不能够跳过,例如:

[a,a,b] 

我们开始以第一个a作为起始字符的时候,第二个a会被添加到待输出字符串内。但是在以第二个a为首字符的时候,之后的所有组合情况都已经在上一次计算过了,所以可以不用再计算了。这就是可以剪枝的地方。下面的程序就是对原始的字符串进行了字符排序,如果当前字符和前一个字符是一样的,就剪枝跳过。


class Solution:
    def permutation(self, s: str) -> List[str]:
        if not s: return [""]
        s = [s[i] for i in range(len(s))]
        sorted_s = sorted(s)
        out = []

        def backtrack(s, res):
            if not s:#所以不可能有重复的字符串
                out.append(res)
            for i in range(len(s)):
                if i > 0 and s[i] == s[i-1]:#剪枝跳过的条件
                    continue
                else:
                    backtrack(s[:i]+s[i+1:], res+s[i])
        backtrack(sorted_s, "")
        return out
        

还是看看大佬的思路:
这里的剪枝我们可以先不排序,做出一个集合记录之前谁用过的字符,如果出现重复的字符,就跳过,少一个排序,相邻元数比较的过程。

class Solution:
    def permutation(self, s: str) -> List[str]:
        self.res = []
        n = len(s)

        def backtrack(s, path):
            if not s:
                self.res.append(path)
            seen = set()
            for i in range(len(s)):
                if s[i] in seen: continue
                seen.add(s[i])
                backtrack(s[:i]+s[i+1:], path + s[i])

        backtrack(s, "")
        return self.res

你可能感兴趣的:(leetcode,字符串,剪枝,python,算法)