《编程思维与实践》1039.字符组合
题目
思路
先将字符串去重+排序(保证每个组合中的字符按字典序),然后枚举出所有组合的情形,最后再进行字典序排序即可.
其中字符串的去重排序可以利用ASCII码值进行桶排序,关键在于如何枚举所有组合的情形.
每个位置有两种可能(选或不选),但至少要选一个,所以一共有 2 n − 1 2^{n}-1 2n−1种(减去都不选的情况)可能性.
不妨将选记为1,不选记为0,那么这就刚好与二进制是对应的:
以abc的所有组合为例,
000表示abc都不选 , 001对应c , 010对应b , 011对应bc , 100对应a , 101对应ac , 110对应ab , 111对应abc.
代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int cmp(const void* a,const void* b) //字典序排序
{
char* m=(char*)a;
char* n=(char*)b;
return strcmp(m,n);
}
int main()
{
int T;
scanf("%d",&T);
for(int i=0;i<T;i++)
{
char s[17]; //长度不超过16
scanf("%s",s);
char differ[17]={'\0'}; //桶
int count=0;
for(int j=0;j<strlen(s);j++)
{
if(strchr(differ,s[j])==NULL)
{
differ[count++]=s[j];
}
}
differ[count]='\0';
qsort(differ,count,sizeof(char),cmp);
int temp=(1<<count)-1; //所有组合结果数
char result[temp][17]; //所有组合情形
int possible[count]; //二进制枚举
memset(possible,0,sizeof(possible));
for(int j=0;j<temp;j++) //每位依此加1
{
possible[0]++;
for(int k=0;k<count-1;k++)
{
if(possible[k]==2) //进位
{
possible[k]=0;
possible[k+1]++;
}
}
int t=0;
for(int k=0;k<count;k++)
{
if(possible[k])
{
result[j][t++]=differ[k];
}
}
result[j][t]='\0';
}
qsort(result,temp,sizeof(result[0]),cmp); //排序
printf("case #%d:\n",i);
for(int j=0;j<temp;j++)
{
printf("%s\n",result[j]);
}
}
return 0;
}