字符串的排列

字符串的排列

描述
输入一个长度为 n 字符串,打印出该字符串中字符的所有排列,你可以以任意顺序返回这个字符串数组。
例如输入字符串ABC,则输出由字符A,B,C所能排列出来的所有字符串ABC,ACB,BAC,BCA,CBA和CAB。
在这里插入图片描述

数据范围:n<10
要求:空间复杂度 O(n!),时间复杂度 O(n!)
输入描述:
输入一个字符串,长度不超过10,字符只包括大小写字母。
示例1
输入:
“ab”

返回值:
[“ab”,“ba”]

说明:
返回[“ba”,“ab”]也是正确的
示例2
输入:
“aab”

返回值:
[“aab”,“aba”,“baa”]

示例3
输入:
“abc”

返回值:
[“abc”,“acb”,“bac”,“bca”,“cab”,“cba”]

示例4
输入:
“”

返回值:
[]

方法一:递归+回溯

思路:
最直接的办法就是固定字符串中一个字符,然后交换另外两个字符,这样从头到尾遍历;用递归:
递归步骤为:

  • 设递归函数dfs(int pos,string s)表示固定字符串s下标为pos的字符是s[pos]
  • 递归终止条件是pos+1==s.size(),表示对最后一个字符完成了固定,这样此时可以返回结果s
  • 下一次继续递归dfs(pos+1,s),表示对下一个字符进行固定
    但是这样做的结果时会有重复的可能,因此利用集合set来去重
    代码如下:
class Solution {
public:
    void perm(int pos,string& s,unordered_set<string>& st){
        if(pos+1==s.size()){
            st.insert(s);
            return ;
        }
        //例如"ABC"第一次交换'A'和'A'则表示固定'A'
        //第二次交换'A'和'B'则表示固定'B'
        //第三次交换'A'和'C'则表示固定'C'
        for(int i=pos;i<s.size();++i){
            swap(s[pos], s[i]);
            perm(pos+1, s, st);
            swap(s[pos], s[i]);//这里必须回溯,否则会得不到部分结果
        }
    }
    vector<string> Permutation(string str) {
        if(str=="") return {};
        unordered_set<string> st;
        perm(0, str, st);
        return vector<string>(st.begin(),st.end());
    }
};
  • 时间复杂度: O(n!)
  • 空间复杂度: O(n!)

方法二:next_permutation

C++11标准库中有一个函数next_permutation()可以得到当前字符串下一个字典序较大的排列,那么我们只要不断的对字符串进行next_permutation()就可以获得所有排列,下面介绍next_permutation();

  • next_permutation()的思想就是从字符串的末尾向前,找到第一个第i个元素的值小于第i+1个元素的值,这样从第i+1个元素到末尾(即[i+1,n))构成一个非升序序列;
  • 然后再从末尾找到第一个大于第i个元素值得元素,并交换两者,而且[j+1,n)上所有元素都不大于第i个元素的值;
  • 因此最后只需翻转[i+1,n)区间则可以得到字符串字典序的下一个排列,即结果。
class Solution {
public:
    vector<string> ans;
 
    vector<string> Permutation(string str) {
    	if(str=="") return ret;
        sort(str.begin(), str.end());    // 一定记住要先排序
        do{
            ans.push_back(str);
        } while(nextPermutation(str));
        return ans;
    }
 
    bool nextPermutation(string& s){
        int i = s.size() - 2;    // 从倒数第二个数开始向左搜寻
        while(i >= 0 && s[i] >= s[i + 1]) i--;   
        if(i < 0) return false;    // 未找到满足条件的i, 返回false
        int j = s.size() - 1;
        while(s[j] <= s[i]) j--;    // 若i存在, 则j一定存在
        swap(s[i], s[j]);
        reverse(s.begin() + i + 1, s.end());    // 翻转区间[i + 1, n - 1]
        return true;
    }
};
  • 时间复杂度: 排序的复杂度是O(NlogN),而遍历序列的复杂度是O(N!),因此最后的复杂度为O(N!);
  • 空间复杂度: 没有用到额外的空间,为O(1).
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值