https://www.51nod.com/Challenge/Problem.html#!#problemId=1384
给出一个字符串S(可能有重复的字符),按照字典序从小到大,输出S包括的字符组成的所有排列。例如:S = “1312”,

字典序法

设P是集合{1,2,……n-1,n}的一个全排列:P=P1P2……Pj-1PjPj+1……Pn(1≤P1,P2,……,Pn≤n-1)
从排列的右端开始,找出第一个比右边数字小的数字的序号j,即> j=max{i|Pi<Pi+1,i>j}
在Pj的右边的数字中,找出所有比Pj大的数字中最小的数字Pk,即k=min{i|Pi>Pj,i>j}
交换Pi,Pk
再将排列右端的递减部分Pj+1Pj+2……Pn倒转,因为j右端的数字是降序,所以只需要其左边和右边的交换,直到中间,因此可以得到一个新的排列P’=P1P2……Pj-1PkPn……Pj+2Pj+1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
char str[10];
int a[10],len;
void reverse(int a[],int l,int r){
for(int i=0;i<(r-l+1)/2;i++){
swap(a[i+l],a[r-i]);
}
}
void print(int a[],int len){
for(int k=0;k<len;k++){
putchar(a[k]+'0');
}
putchar('\n');
}
void permutation(int a[],int len){
sort(a,a+len);
print(a,len);
int i,j;
while(true){
i=len-2;
while(a[i]>=a[i+1]&&i>=-1)i--;
if(i==-1)break;
j=i+1;
while(a[j]>a[i]&&j<len)j++;
swap(a[i],a[j-1]);
reverse(a,i+1,len-1);

print(a,len);
}
}
int main(){
freopen("in","r",stdin);
scanf("%s",str);
len=strlen(str);
for(int i=0;i<len;i++){
a[i]=str[i]-'0';
}
permutation(a, len);
return 0;
}