https://www.nowcoder.com/acm/contest/76/H
给一道题,可以去测试代码。
这里总结一下全排列的几种方法:
方法一:利用交换排列:缺点:不能按字典序排列,但可以借助set处理。
#include <bits/stdc++.h> //不会按字典序排列
using namespace std;
void pre(string str, int s, int e){
if(s == e){
cout << str << endl;
}
else{
for(int i = s; i <= e; i++){
if(i != s && str[i] == str[s]) //去重,除了第一位,其余的相同时无须交换
continue;
swap(str[i], str[s]);
pre(str, s + 1, e);
swap(str[i], str[s]);
}
}
}
int main(){
string str;
cin >> str;
pre(str, 0, str.length() - 1);
return 0;
}
方法二:利用dfs,只能处理没有重复元素的字符串
#include <bits/stdc++.h>
using namespace std;
char str[100];
char mu[100];
void dfs(int len, int n){
if(len == n){
cout << mu << endl;
return ;
}
for(int i = 0; i < n; i++){ //尝试放入每个字符
int flag = 1;
for(int j = 0; j < len; j++){ //检测这个字符是否已经放入过
if(mu[j] == str[i]){
flag = 0;
break;
}
}
if(flag){
mu[len] = str[i];
dfs(len + 1, n);
}
}
}
int main(){
cin >> str;
sort(str, str + strlen(str));
dfs(0, strlen(str));
return 0;
}
方法三:dfs: 感觉这个是比较理想的,可以按字典序排好输出,并能去重,一定要掌握!!!
对于A1,B,A2进行排序,(这里的A1, A2代表相同元素,下标是为了区分)
首先sort一下变成:A1,A2,B
然后第一趟:
A1,A2,B
A1, B, A2
然后遍历到A2开头了,好,后面查找下一个时,从i=0开始,
于是判断A1是否访问过,未访问则进行最关键的步骤了:
检测A1之后是否有已访问的且和A1相等,如若是则放弃A1继续遍历。
这里有点不好理解,对于A1A2B的序列,可知A1再前所以以A开头的
必定是以A1开头,所以在尝试A2开始头时,遍历尝试放入第二个元
素时判断A1虽然当前未访问过,但他是在A2之前,一定在之前的全
排列之中了,故放弃。
感觉还是不太好理解;还是自己在纸上画画,模拟一下,多体会体会。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3;
char str[N];
char buf[N];
int toal, len;
bool visit[N];
void pre(int num){
if(num == len){
cout << buf << endl;
toal++;
return ;
}
for(int i = 0; i < len; i++){
if(!visit[i]){
bool flag = 1;
for(int j = i + 1; j < len; j++){
if(visit[j] && str[j] == str[i]){ //检测重复
flag = 0;
break;
}
}
if(flag){
buf[num] = str[i]; //这里不能写成:buf[num++] = str[i];因为for循环没有结束还要用num,不能改变num的值
visit[i] = 1;
pre(num + 1);
visit[i] = 0;
}
}
}
}
int main(){
cin >> str;
len = strlen(str);
sort(str, str + len);
pre(0);
cout << "toal: " << toal << endl;
return 0;
}