[Mdfs] lc剑指 Offer 38. 字符串的排列(全排列+枚举顺序+组合类型枚举+知识理解+模板题)

Posted Ypuyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Mdfs] lc剑指 Offer 38. 字符串的排列(全排列+枚举顺序+组合类型枚举+知识理解+模板题)相关的知识,希望对你有一定的参考价值。

1. 题目来源

链接:剑指 Offer 38. 字符串的排列

相关:

2. 题目解析

就是个全排列的去重,也可称为组合类型枚举。和 [Mdfs] lc47. 全排列 II(dfs+去重处理+经典) 一模一样。

排序,保证相同元素的相对位置顺序不发生改变就能去重。 基于此,一般可以枚举每个数放的位置,也可以枚举每个位置可以放哪些数。

当然直接使用 next_permutation() 函数也是可以的。

迭代写法就是实现一个 next_permutation() 的操作,[M模拟] lc31. 下一个排列(模拟next_permutation函数+思维) 类比这个模拟实现即可。


在此,给出了本题的两种枚举方式:

  • 枚举位置放哪些数
  • 枚举数放在哪些位置

这两种枚举方式以及细节,都是为了保证相同数的相对位置不发生改变。

其中,需要特别关注 int u 的含义是截然不同的!


时间复杂度: O ( n ! ) O(n!) O(n!)

空间复杂度: O ( n ) O(n) O(n)


每个位置,可以放哪些数:

这个主要保证:如果前一个数跟当前数相同,但是前一个数还没放置时,那么当前数一定不能放,因为一旦放置的话,我们是顺序枚举位置的,就会造成相对位置发生改变!

class Solution {
public:
    vector<string> res;
    vector<bool> st;
    string path;
    void dfs(int u, string s) {
        if (u == s.size()) {
            res.push_back(path);
            return ;
        }

        for (int i = 0; i < s.size(); i ++ ) {
            if (!st[i]) {
                if (i && s[i - 1] == s[i] && !st[i - 1]) continue;
                path += s[i];
                st[i] = true;
                dfs(u + 1, s);
                st[i] = false;
                path.pop_back();
            }
        }
    }
    vector<string> permutation(string s) {
        sort(s.begin(), s.end());
        st = vector<bool>(s.size(), false);
        dfs(0, s);
        return res;
    }
};

每个数,可以放哪些位置:

这个写的比较少,但是也是需要理解,大同小异。需要保证相同数的相对位置不变。如果下一个数和当前数是相同的,那么下一个数的起始位置必须在当前数后,而不能在当前数之前。故需要一个变量记录上一个相同数的放置位置,也就是枚举组合时常见的 start 变量。在枚举下一个相同数时,它的放置位置就只能是 start+1, start+2…,而不能在 start 之前。

class Solution {
public:
    vector<string> res;
    vector<bool> st;
    string path;
    void dfs(int u, string s, int start) {
        if (u == s.size()) {
            res.push_back(path);
            return ;
        }

        for (int i = start; i < s.size(); i ++ ) {
            if (!st[i]) {
                path[i] = s[u];
                st[i] = true;
                if (u + 1 < s.size() && s[u + 1] != s[u]) dfs(u + 1, s, 0); // 不同数,下次从 0 开始放
                else dfs(u + 1, s, i + 1);      // 相同数,只能放到 i 的后面
                st[i] = false;
            }
        }
    }
    vector<string> permutation(string s) {
        sort(s.begin(), s.end());
        st.resize(s.size());
        path.resize(s.size());
        dfs(0, s, 0);
        return res;
    }
};

库函数

class Solution {
public:
    vector<string> permutation(string s) {
        sort(s.begin(), s.end());
        vector<string> res;
        do {
            res.push_back(s);
        } while(next_permutation(s.begin(), s.end()));

        return res;
    }
};

以上是关于[Mdfs] lc剑指 Offer 38. 字符串的排列(全排列+枚举顺序+组合类型枚举+知识理解+模板题)的主要内容,如果未能解决你的问题,请参考以下文章

Lc_剑指Offer15二进制中1的个数--------位运算

剑指offer--38字符串的排列

剑指offer--38字符串的排列

力扣算法JS LC [347. 前 K 个高频元素] LC [剑指 Offer 10- I. 斐波那契数列]

剑指 Offer 38. 字符串的排列

剑指 Offer 38. 字符串的排列