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