全排列问题初探,不含重复元素情况的讨论。
糊的题目:
【题目描述】
给定一个由不同的小写字母组成的字符串,输出这个字符串的所有全排列。
我们假设对于小写字母有‘a’ <‘b’ < ... <‘y’<‘z’,而且给定的字符串中的字母已经按照从小到大的顺序排列。
【输入】
只有一行,是一个由不同的小写字母组成的字符串,已知字符串的长度在1到6之间。
【输出】
输出这个字符串的所有排列方式,每行一个排列。要求字母序比较小的排列在前面。字母序如下定
义:
已知S=s1s2...sk,T=t1t2...tkS=s1s2...sk,T=t1t2...tk,则S<T等价于,存在p(1≤p≤k),使得s1=t1,s2=t2,...,sp−1=tp−1,sp<tps1=t1,s2=t2,...,sp−1=tp−1,sp<tp成立。
【输入样例】
abc
【输出样例】
abc
acb
bac
bca
cab
cba
看了帖子也看了书,最后把当时的代码都糊上来了,因为懒就直接ctrl+/把其他的写法个注释掉了。需要的话,可以全部copy下来斟酌一番,考虑区别与不同,会有不小收获。
按字典序的代码实现这里用的是深搜,每次调用后从0开始,先后搜索原序列,要么赋值要么记录下标,最后到临界条件一并输出。
而不能字典序的递归回溯写法,递归的函数f(n)的意义是——输出第n+1位即其后的所有全排列序。是通过for循环对原序列每个位子上的所有情况与后面原素交换位置来实现的,其中通过标记来记录是否选择过,且由于交换的规则是与该次调用中for循环里的a[i]交换,每次换位调用后需恢复,并进行下一次换位。
这个方法可以参考下面的博客。https://blog.csdn.net/summerxiachen/article/details/60579623
需注意的是得思考为什么这样无法实现每次输出都按照字典序,由于交换代码的实现,拿三个以元素abc为原序列为例,当第一个位子的元素于三个位子元素交换后(也就是当前n == 0, 第二个for循环里的i等于2时)就直接往后调用f(0+1),而这一次的输出效果就是cba,而不是我们想要的先输出cab。其中本质的原因在于f(n)函数中每次n与跨k(k>=1)个元素交换位置时(循环到i == n+k时),直接就向后递归,然后第一次交换后直接就以一种情况先输出,再来回溯,继续判断符合条件输出。这样输出的顺序就会与字典需不相符。(换个角度其实可以看出其整体的交换输出方式,就像一个输出模板,内部数字小标是123 132 213 231 321 312依次将第一个位子的元素与2、3交换后,然后直接输出a[1]a[2]a[3] a[1]a[3]a[2] a[2]a[1]a[3] ...挺有意思。
表达能力有限,可能讲得不够清楚,(虽然我就是这么想的,) 看到了要是让你有疑惑,可以指出(当然更多的是仔细再想想
我尽力讨论清楚。
//#include<iostream>
//#include<cstring>
//#include<cstdio>
//using namespace std;
//char ans[101], s[101];
//bool b[123];//由于b[a[i]],a[i]字母范围是97~122。
//int len;
//
//void dfs(int n) {
// if(n == len) {
// for(int i = 0; i < len; ++i)
// cout << ans[i];
// cout << endl;
// }
// else //考虑有else与没else的区别#1
// for(int j = 0; j < len; j++){
// if(!b[s[j]]){
// b[s[j]] = 1;
// ans[n] = s[j];
// dfs(n+1);
// b[s[j]] = 0;
// }
// }
//}
//int main() {
// cin >> s;
// len = strlen(s);
// dfs(0);
//}
/*
极简版
*/
//#include<iostream>
//#include<cstring>
//using namespace std;
//char s[100];
//int c[100], b[100];//记录排好序的字母的下标顺序
//int len;
//
//int dfs(int n) {
// if(n == len){
// for(int i = 0; i < len; i++)
// cout << s[c[i]];
// cout << endl;
// }
// for(int i = 0; i < len; i++) {
// if(!b[i]) {
// b[i] = 1;
// c[n] = i;
// dfs(n+1);
// b[i] = 0;
// }
// }
//}
//int main() {
// cin >> s;
// len = strlen(s);
// dfs(0);
//}
#include<iostream>
#include<cstring>
using namespace std;
char s[100];
int c[100], b[100];//记录排好序的字母的下标顺序
int len;
int dfs(int n) {
for(int i = 0; i < len; i++) {
if(!b[i]) {
b[i] = 1;
c[n] = i;
dfs(n+1);
b[i] = 0;
}
}
if(n == len - 1){//if可以写到后面,但条件需变动,想想本质的区别#2
for(int i = 0; i < len; i++)
cout << s[c[i]];
cout << endl;
}
}
int main() {
cin >> s;
len = strlen(s);
dfs(0);
}
//#include<iostream>
//#include<cstring>
//using namespace std;
//char s[101];
//int c[101], b[101], len;
//int dfs(int n) {
// for(int i = 0; i < len; ++i) {
// if(b[i] == 0) {
// b[i] = 1;
// c[n] = i;
// if(n == len - 1){
// for(int i = 0; i < len; i++)
// cout << s[c[i]];
// cout << endl;
// }
// else
// dfs(n+1);
// b[i] = 0;
// }
// }
//}
//int main(){
// cin >> s;
// len = strlen(s);
// dfs(0);
//}
//#include<iostream>
//#include<cstring>
//using namespace std;
//char a[100];
//int len;
不能字典序呈现
//void swap(char *a, char *b) {
// char temp;
// temp = *a;
// *a = *b;
// *b = temp;
//}
//int f(int n){
// if(n == len) {
// for(int i = 0; i < len; ++i)
// cout << a[i];
// cout << endl;
// }
// for(int i = n; i < len ; i++) {
// swap(a[i], a[n]);
// f(n+1);
// swap(a[i], a[n]);
// }
//}
//
//int main() {
// scanf("%s", a);
// len = strlen(a);
// f(0);
//}
//下面这时错误的代码,最开始,思路不清晰时写下的,回过头来看可以反映些问题
//#include<cstring>
//using namespace std;
//
//int a[5] = {0};
//int k = 0;
//
//void f(int n) {
// while(k < 5 && a[n] == 1) {
// n = (n + 1) % 5;
// k++;
// }
// if(k == 5) {
// cout << endl;
// k = 0;
// memset(a, 0, sizeof(a));
// return;
// }
// cout << n;
// k++;
// for(int i = n; i < 5; i++) {
// a[n] = 1;
// f(n+1);
// a[n] = 0;
// }
//
//}
//
//int main() {
// f(0);
//}
思考:
#1:不管由还是没有if都会执行,但判断成立后,没else则会继续执行后面的语句,
虽然这里没影响但如果本意不希望再执行下面的代码则需用else.
#2当当前参数n等于len-1时,也就意味这上面的for循环将填满最后一个位置的数,
for循环结束后就得输出结果(这里再次体现该函数的意义——在a[i]的第n位(0~len-1)上填满足条件的数.
//小经验:
//1.当被变量弄混淆时,需弄清各变量在代码中的含义 ,各施其职,得分清后再考虑去,一语双关。
//2.递归实现时,的结果输出需注意,你确定是要在每次调用过程中同时来输出答案吗(还希望每次换行后,
//当前输出的前面自动cout出之前的结果。)?还是整理好思路方法,一并输出答案呢。