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;
}