字符串的排列

字符串的排列

http://www.nowcoder.com/questionTerminal/4f31423f126749ab9196c97c5117bcb9

题解:

考察点:深度优先搜索,回溯,剪枝

易错点:

字符串中的字母有重复,直接使用全排列生成的字符串会有重复,需要通过剪枝或者等手段去重。

方法一:回溯+去重

回溯法是一种深度优先搜索中常用的一种手段,基本思想是首先按选设定条件进行深度搜索,当探索到某一步时,发现原先搜索的路径并不满足,就退回上一步重新选择。在全排列问题中,首先设置一个指针,代表当前生成的字符串中的元素个数,设置一个数组表示每个位置是否被访问过,然后按照从前往后的顺寻,选择未被访问过的字母进入字符串,每次被选择的位置把标记为已访问,在进行深度优先搜索之后再将标记释放,进行回溯。注意,因为字母有重复,因此生成的全排列也会有重复,需要进行去重。

#include "bits/stdc++.h"
using namespace std;
string s;
int vis[10];
void dfs(set<string>&res,string ans,vector<int> num,int pos){
    if(pos==num.size()){
        res.insert(ans);
    }
    for(int i=0;i<num.size();i++){
        if(!vis[i]){
            vis[i]=1;
            ans.push_back(num[i]+'a');
            dfs(res,ans,num,pos+1);
            vis[i]=0;
            ans.pop_back();
        }
    }
}
int main()
{
    cin>>s;
    vector<int>num;
    for(int i=0;i<s.length();i++) num.push_back(s[i]-'a');
    set<string>res;
    string ans="";
    dfs(res,ans,num,0);
    int tmp=0;
    int len=res.size();
    for(auto s:res){
        ++tmp;
        if(tmp==1){
            cout<<"["<<s<<", ";
        }else if(tmp==len){
            cout<<s<<"]"<<endl;
        }else{
            cout<<s<<", ";
        }
    }
    return 0;
}

方法二:回溯+剪枝

可以对上述方法进行改进,由于有重复字母存在,可以考虑生成答案的过程中对重复字母进行剪枝。对于两个重复元素,在全排列中交换其位置不会生成新的排列,基于这个思想,首先会对字母进行排序,这样确保重复元素相邻。那么假设当前位置为,在枚举排列的时候,保证当前位置的字母和之前的一样,并且之前的字母没有被标记,则可以把这种情况剪枝掉。

#include "bits/stdc++.h"
using namespace std;
string s;
int vis[10];
void dfs(vector<string>&res,string ans,vector<int> num,int pos){
    if(pos==num.size()){
        res.push_back(ans);
    }
    for(int i=0;i<num.size();i++){
        if(!vis[i]){
            if(i>0&&num[i]==num[i-1]&&!vis[i-1]) continue;
            vis[i]=1;
            ans.push_back(num[i]+'a');
            dfs(res,ans,num,pos+1);
            vis[i]=0;
            ans.pop_back();
        }
    }
}
int main()
{
    cin>>s;
    vector<int>num;
    for(int i=0;i<s.length();i++) num.push_back(s[i]-'a');
    sort(num.begin(),num.end());
    vector<string>res;
    string ans="";
    dfs(res,ans,num,0);
    for(int i=0;i<res.size();i++){
        if(i==0){
            cout<<"["<<res[i]<<", ";
        }else if(i==res.size()-1){
            cout<<res[i]<<"]"<<endl;
        }else{
            cout<<res[i]<<", ";
        }
    }
    return 0;
}
全部评论

相关推荐

不愿透露姓名的神秘牛友
2025-12-17 16:48
今天九点半到公司,我跟往常一样先扫了眼电脑,屁活儿没有。寻思着没事干,就去蹲了个厕所,回来摸出手机刷了会儿。结果老板刚好路过,拍了我一下说上班别玩手机,我吓得赶紧揣兜里。也就过了四十分钟吧,我的直属领导把我叫到小隔间,上来就给我一句:“你玩手机这事儿把老板惹毛了,说白了,你可以重新找工作了,等下&nbsp;HR&nbsp;会来跟你谈。”&nbsp;我当时脑子直接宕机,一句话都没憋出来。后面&nbsp;HR&nbsp;找我谈话,直属领导也在旁边。HR&nbsp;说我这毛病不是一次两次了,属于屡教不改,不光上班玩手机,还用公司电脑看论文、弄学校的事儿。我当时人都傻了,上班摸鱼是不对,可我都是闲得发慌的时候才摸啊!而且玩手机这事儿,从来没人跟我说过后果这么严重,更没人告诉我在公司学个习也算犯错!连一次口头提醒都没有,哪儿来的屡教不改啊?更让我膈应的是,昨天部门刚开了会,说四个实习生里留一个转正,让大家好好表现。结果今天我就因为玩手机被开了。但搞笑的是,开会前直属领导就把我叫去小会议室,明明白白告诉我:“转正这事儿你就别想了,你的学历达不到我们部门要求,当初招你进来也没打算给你这个机会。”合着我没入贵厂的眼是吧?可我都已经被排除在转正名单外了,摸个鱼至于直接把我开了吗?真的太离谱了!
rush$0522:转正名单没进,大概率本来就没打算留你
摸鱼被leader发现了...
点赞 评论 收藏
分享
评论
2
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务