【剑指Offer】**字符串的排列(不重复的全排列组合:dfs在已经固定了前pos个字符的情况下,遍历确定确定下标为pos的新字符,然后继续对后面的字符进行排列组合)

题目描述

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

输入描述:

输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。

DFS思路

核心要点

  1. dfs(str, pos):
    在已经固定了前pos个字符的情况下,dfs对后面的字符串进行排列组合,即[pos,str.length()]
  2. 怎么排列组合:
    通过确定首位字符(即下标为pos的字符),然后继续对后面的字符串进行排列组合
  3. 怎么确定首位str[pos]:
    [pos,str.length()]中的每个字符按顺序站一次首位,注意判断是否和之前站过首位的重复了
  • 时间复杂度:O(n!),n是指字符串长度,时间复杂度和字符串排列的方案数成线性关系n*(n-1)*…*1
  • 空间复杂度:O(n),全排列的递归深度为 N ,系统累计使用栈空间大小为 O(N)

去重的全排列就是从第一个数字起,每个数分别与它后面非重复出现的数字交换
对于字符串的排列问题:
如果能生成n-1个元素的全排列,就能生成n个元素的全排列。对于只有一个元素的集合,可以直接生成全排列。所以全排列的递归终止条件很明确,只有一个元素时。我们可以分析一下全排列的过程:

  1. 首先,我们固定第一个字符a,求后面两个字符bc的排列
  2. 当两个字符bc排列求好之后,我们把第一个字符a和后面的b交换,得到bac,接着我们固定第一个字符b,求后面两个字符ac的排列
  3. 现在是把c放在第一个位置的时候了,但是记住前面我们已经把原先的第一个字符a和后面的b做了交换,为了保证这次c仍是和原先处在第一个位置的a交换,我们在拿c和第一个字符交换之前,先要把b和a交换回来。在交换b和a之后,再拿c和处于第一位置的a进行交换,得到cba。我们再次固定第一个字符c,求后面两个字符b、a的排列
  4. 既然我们已经知道怎么求三个字符的排列,那么固定第一个字符之后求后面两个字符的排列,就是典型的递归思路了
class Solution {
public:
    //去重的全排列就是从第一个数字起,每个数分别与它后面非重复出现的数字交换
    vector<string> Permutation(string str) {
        if(str=="")
            return anws;
        //在已经固定了前0个字符的情况下,dfs对后面的字符串进行排列组合,即[0,str.length()]
        dfs(str, 0); 
        //保证按字典序打印
        sort( anws.begin(), anws.end() );
        return anws;
    }
    /* 
    1、dfs(str, pos): 
    在已经固定了前pos个字符的情况下,dfs对后面[pos,str.length())的字符串进行排列组合
    2、怎么排列组合:
    通过确定首位字符(即下标为pos的字符),然后继续对后面的字符串进行排列组合
    3、怎么确定首位str[pos]:
    [pos,str.length())中的每个字符按顺序站一次首位,注意判断是否和之前站过首位的重复了
    */
    void dfs(string str, int pos){
        if( pos == str.length() ){ //已固定的位置和字符串长度相等,说明一个新的排列已经完成,保存
            anws.push_back(str);
            return;
        }
        //确定首位字符str[pos]:[pos,str.length())中的每个字符按顺序站一次首位
        for(int i=pos; i<str.length(); i++){ 
            //判断当前准备站首位的字符str[i],是否和之前站过首位str[pos,i)的重复了
            if( isRepeated(str,pos,i) ) 
                continue;
            //当前这个非重复的字符str[i]交换到pos的位置
            swap(str[pos], str[i]); 
            // 继续对后面的字符串进行排列组合
            dfs(str, pos+1); 
            //完成一次站首位并排列组合后,记得回退操作,恢复str的字符顺序,准备下一个字符的站首位
            swap(str[pos], str[i]); 
        }
    }
private:
    vector<string> anws;
    //判断字符串str[start,pos)中是否存在str[pos]的重复字符
    bool isRepeated(string str, int start, int pos ){
        for( int i = start; i < pos; i++ ){
            if( str[i] == str[pos] )
                return true;
        }
        return false;
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值