Go&Java算法之字符串的排列

158 阅读1分钟

这是我参与2022首次更文挑战的第20天,活动详情查看:2022首次更文挑战

字符串的排列

输入一个字符串,打印出该字符串中字符的所有排列。

 

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

 

示例:

输入:s = "abc"

输出:["abc","acb","bac","bca","cab","cba"]  

限制:

1 <= s 的长度 <= 8

题解

算法一:DFS回溯(Java)

本题是回溯算法经典题目,求全排列+去重

这道题目和全排列的区别在与给定一个可包含重复数字的序列,要返回所有不重复的全排列。

这里又涉及到去重了。

那么排列问题其实也是一样的套路。

还要强调的是去重一定要对元素经行排序,这样我们才方便通过相邻的节点来判断是否重复使用了。

对于一个长度为 n 的字符串(假设字符互不重复),其排列方案数共有:

n×(n1)×(n2)×2×1n \times (n-1) \times (n-2) … \times 2 \times 1

根据字符串排列的特点,考虑深度优先搜索所有排列方案。即通过字符交换,先固定第 1 位字符( n 种情况)、再固定第 2 位字符( n-1 种情况)、... 、最后固定第 n 位字符( 1 种情况)。

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

class Solution {
       public String[] permutation(String s) {
        if (s == null || s.length() == 0) {
            return new String[0];
        }

        List<String> res = new ArrayList<>();
        StringBuilder sb = new StringBuilder();
        char[] chars = s.toCharArray();
        boolean[] visited = new boolean[chars.length];

        Arrays.sort(chars);
        permutation2Helper(res, sb, chars, visited);

        return res.toArray(new String[res.size()]);
    }

    private void permutation2Helper(List<String> res, StringBuilder sb, char[] chars, boolean[] visited) {
        if (sb.length() == chars.length) {
            res.add(String.valueOf(sb));
        }

        for (int i = 0; i < chars.length; i++) {
            if (visited[i]) {
                continue;
            }
            if (i > 0 && chars[i] == chars[i - 1] && !visited[i - 1]) {
                continue;
            }
            visited[i] = true;
            sb.append(chars[i]);
            permutation2Helper(res, sb, chars, visited);
            sb.deleteCharAt(sb.length() - 1);
            visited[i] = false;
        }
    }
}

时间复杂度:O(N*N!)

空间复杂度:O(N)

算法二:DFS回溯(Go)

思路同上

func permutation(s string) (ans []string) {
    t := []byte(s)
    sort.Slice(t, func(i, j int) bool { return t[i] < t[j] })
    n := len(t)
    perm := make([]byte, 0, n)
    vis := make([]bool, n)
    var backtrack func(int)
    backtrack = func(i int) {
        if i == n {
            ans = append(ans, string(perm))
            return
        }
        for j, b := range vis {
            if b || j > 0 && !vis[j-1] && t[j-1] == t[j] {
                continue
            }
            vis[j] = true
            perm = append(perm, t[j])
            backtrack(i + 1)
            perm = perm[:len(perm)-1]
            vis[j] = false
        }
    }
    backtrack(0)
    return
}

时间复杂度:O(N*N!)

空间复杂度:O(N)