题干
对如非全键盘的手机上的数字,每个数字都对应一些字母,比如2对应ABC,3对应DEF.........,8对应TUV,9对应WXYZ,要求对一段数字,输出其代表的所有可能的字母组合,如5869,可能代表JTMW、JTMX.................
我的解题思路
刚开始我还以为本题会给我一个多么惊人的答案,结果发现,也只是穷举而已。尽管如此,本题虽然给了伪代码,但是还是比较难看懂,本着“纸上得来终觉浅 绝知此事要躬行”的要义,我决定自己做一遍。实际上刚开始也想了很久,总以为有什么技巧,哪里知道就是穷举而已。
难点就在于事先不知道输入多长的数字,如果只有两位,两个for循环,如果有三位,就三个for循环。但是在不知道的情况下,我设计出了我的解题思路,下面开始说明:
对于数字:23,共有9种可能(根据组合知识可知),如下所示
- ad
- ae
- af
- bd
- be
- bf
- cd
- ce
- cf
对于数字:234,共有下面27种可能,如下所示,
- adg
- adh
- adi
- aeg
- aeh
- aei
- afg
- afh
- afi
- bdg
- bdh
- bdi
- beg
- beh
- bei
- bfg
- bfh
- bfi
- cdg
- cdh
- cdi
- ceg
- ceh
- cei
- cfg
- cfh
- cfi
可以发现,在234中,由左到右的第三列直接,由上到下,只需要按照ghi的顺序一直重复27下,第二列则是3次d,3次e,3次f,而第一列则是9次a、9次b、9次c。这是有规律的。
规律就是:左到右的最后一列,只需要按照其数字代表的字母一直重复就可以了,而倒数第二列,重复的次数则是前一列数字代表的字符的个数,再下一列每个字符重复的次数就是前几列数字代表字符个数的乘积。按照这个规律,我写出了代码,并设计了测试用列。
成功而冗余的版本
#include <stdio.h>
#include <string.h>
void getwords(char *numstr){
int num=atoi(numstr); //把输入的字符转化为int类型
if(num==0||num==1)return; //可以返回一些没有意义一些情况
//初始化的准备
char c[10][10]={
"", //0
"", //1
"abc", //2
"def", //3
"ghi", //4
"jkl", //5
"mno", //6
"pqrs", //7
"tuv", //8
"wxyz" //9
};
int total[10]={0,0,3,3,3,3,3,4,3,4};
int resultNum=1; //我们要计算总共结果有多少个
int count0And1=0; //记录0和1的个数,因为0和1是没有字母打印的
int length=0;
while(num%10 || num/10){
int temp=num%10;
num/=10;
if(temp==1||temp==0) {
count0And1++;
continue ;
}
resultNum*=total[temp];
length++;
}
if(count0And1==length) return; //全是没有1和0的情况
char result[resultNum][length+1]; //用于存放结果的数组,注意0和1是不占位置的,加1是为了放置\0
num=atoi(numstr);
int h; //h是循环变量
for(h=0;h<resultNum;h++){
result[h][length]='\0'; //给最后一位附上0
}
int count=1; //这个的计算很复杂,它是当前要打印的一列的左边一共有多少种组合,也限制着当前这一列会要重复多少次其元素
int k=0; //k用来由最低位到最高位,设置当前应该打印一列的。
for(h=0;h<length;++h){ //重复次数就是列数,不包括0和1的情况
int lastPosition=num%10; //拿到的是最低位的数字,我们也先放至最低位的字母
num/=10;
if(lastPosition==1||lastPosition==0) { //如果这一位是0或者1,是不会产生字母的
--h;
continue ;
}else{
++k; //如果是0或者1,应该不该打印这一列,所以不要移动k
}
int i,j;
for(j=0,i=-1;j<resultNum;j++){
if(j%count==0){ //同一个元素重复多少次
++i;
}
if(i==total[lastPosition]) i=0;
result[j][length-k]=c[lastPosition][i]; //length是算了0或者1的占位的长度,但是0和1没有字符,那一列不打,k是用于用后往前倒计的
}
if(total[lastPosition]){
count*=total[lastPosition];
}
}
int i;
printf("数字为:%s\n",numstr);
for(i=0;i<resultNum;i++){
printf("%d\t%s\n",i+1,result[i]);
}
}
int main() {
/*下面都是些测试用列*/
getwords(" "); //边界,有空格字符串 我是让其直接返回了
getwords(""); //边界,没有字符串
getwords("0");
getwords("23"); //随意检测
getwords("234"); //随意检测
getwords("229"); //9是含有4个字母的
getwords("922"); //9在首
getwords("292"); //9在首
getwords("122"); //1是没有包含字符的
getwords("221"); //1是没有包含字符的
getwords("212"); //1是没有包含字符的
getwords("022"); //1是没有包含字符的 这里出现的一个bug很有意思,是因为0在最前面,当变成int的时候,这个0就消失了,但是strlen还算会算这个0的占位
getwords("220"); //1是没有包含字符的
getwords("202"); //1是没有包含字符的
}
改进后的版本
/**
* 改进版本有一个关键的想法就是:
* 在传给getwords的字符串中先删除0和1这两个数,
* 这两个数无论存在在哪里都不会影响结果,
* 却会使代码需要写的更为复杂
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/**
* 传入一个数字的字符串
* 会打印该数字在手机键盘上出现的字母的组合
*/
void getwords(char *numstr){
char *number=malloc(1);
size_t i,j,h;//循环用的变量
for(j=0,i=0;j<strlen(numstr);j++){
if(numstr[j]!='1' &&numstr[j]!='0'){
number[i]=numstr[j];
++i;
}
}
number[i]='\0';
int num=atoi(number); //把输入的字符转化为int类型
if(num==0||num==1) return; //可以返回一些没有意义一些情况
//初始化的准备
char c[10][10]={
"", //0
"", //1
"abc", //2
"def", //3
"ghi", //4
"jkl", //5
"mno", //6
"pqrs", //7
"tuv", //8
"wxyz" //9
};
int total[10]={0,0,3,3,3,3,3,4,3,4};
int resultNum=1; //我们要计算总共结果有多少个
int length=0;
while(num%10){
int temp=num%10;
num/=10;
resultNum*=total[temp];
length++;
}
char result[resultNum][length+1]; //加一是加‘\0’的位置
num=atoi(number); //刚刚num被计算过来,这次再次拿到其值
//给最后一位附上0
for(h=0;h<resultNum;h++){
result[h][length]='\0';
}
int count=1; //它是当前要打印的一列的左边一共有多少种组合,也限制着当前这一列会要重复多少次其元素
int k=0; //k用来由最低位到最高位,设置当前应该打印一列
for(h=0;h<length;++h){ //重复次数就是列数
int lastPosition=num%10; //拿到的是最低位的数字,我们也先放至最低位的字母
num/=10;
++k;
int i,j;
for(j=0,i=-1;j<resultNum;j++){
if(j%count==0){ //同一个元素重复多少次
++i;
}
if(i==total[lastPosition]) i=0;
result[j][length-k]=c[lastPosition][i]; //length是算了0或者1的占位的长度,但是0和1没有字符,那一列不打,k是用于用后往前倒计的
}
if(total[lastPosition]){
count*=total[lastPosition];//计算打印了这一列有多少个,会影响下一个循环重复多少次
}
}
//打印结果
printf("数字为:%s\n",numstr);
for(i=0;i<resultNum;i++){
printf("%d\t%s\n",i+1,result[i]);
}
}
int main() {
/*下面都是些测试用列*/
getwords(" "); //边界,有空格字符串 我是让其直接返回了
getwords(""); //边界,没有字符串
getwords("0");
getwords("23"); //随意检测
getwords("234"); //随意检测
getwords("229"); //9是含有4个字母的
getwords("922"); //9在首
getwords("292"); //9在首
getwords("122"); //1是没有包含字符的
getwords("221"); //1是没有包含字符的
getwords("212"); //1是没有包含字符的
getwords("022"); //0是没有包含字符的 并且0这个数字有点特别会影响计算
getwords("220"); //0是没有包含字符的
getwords("202"); //0是没有包含字符的
}
测试结果
asd@asd-desktop:~/workspace/test/src$ ./a.out
数字为:23
1 ad
2 ae
3 af
4 bd
5 be
6 bf
7 cd
8 ce
9 cf
数字为:234
1 adg
2 adh
3 adi
4 aeg
5 aeh
6 aei
7 afg
8 afh
9 afi
10 bdg
11 bdh
12 bdi
13 beg
14 beh
15 bei
16 bfg
17 bfh
18 bfi
19 cdg
20 cdh
21 cdi
22 ceg
23 ceh
24 cei
25 cfg
26 cfh
27 cfi
数字为:229
1 aaw
2 aax
3 aay
4 aaz
5 abw
6 abx
7 aby
8 abz
9 acw
10 acx
11 acy
12 acz
13 baw
14 bax
15 bay
16 baz
17 bbw
18 bbx
19 bby
20 bbz
21 bcw
22 bcx
23 bcy
24 bcz
25 caw
26 cax
27 cay
28 caz
29 cbw
30 cbx
31 cby
32 cbz
33 ccw
34 ccx
35 ccy
36 ccz
数字为:922
1 waa
2 wab
3 wac
4 wba
5 wbb
6 wbc
7 wca
8 wcb
9 wcc
10 xaa
11 xab
12 xac
13 xba
14 xbb
15 xbc
16 xca
17 xcb
18 xcc
19 yaa
20 yab
21 yac
22 yba
23 ybb
24 ybc
25 yca
26 ycb
27 ycc
28 zaa
29 zab
30 zac
31 zba
32 zbb
33 zbc
34 zca
35 zcb
36 zcc
数字为:292
1 awa
2 awb
3 awc
4 axa
5 axb
6 axc
7 aya
8 ayb
9 ayc
10 aza
11 azb
12 azc
13 bwa
14 bwb
15 bwc
16 bxa
17 bxb
18 bxc
19 bya
20 byb
21 byc
22 bza
23 bzb
24 bzc
25 cwa
26 cwb
27 cwc
28 cxa
29 cxb
30 cxc
31 cya
32 cyb
33 cyc
34 cza
35 czb
36 czc
数字为:122
1 aa
2 ab
3 ac
4 ba
5 bb
6 bc
7 ca
8 cb
9 cc
数字为:221
1 aa
2 ab
3 ac
4 ba
5 bb
6 bc
7 ca
8 cb
9 cc
数字为:212
1 aa
2 ab
3 ac
4 ba
5 bb
6 bc
7 ca
8 cb
9 cc
数字为:022
1 aa
2 ab
3 ac
4 ba
5 bb
6 bc
7 ca
8 cb
9 cc
数字为:220
1 aa
2 ab
3 ac
4 ba
5 bb
6 bc
7 ca
8 cb
9 cc
数字为:202
1 aa
2 ab
3 ac
4 ba
5 bb
6 bc
7 ca
8 cb
9 cc
书上while循环完成的代码
/**
* 根据书上221页的伪代码写成。
* 调试着慢慢看吧,两个while循环确实配合的非常好
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void getwords(char *numstr){
int TelLength=strlen(numstr);
int number[TelLength];
int answer[TelLength];
int i;
for(i=0;i<TelLength;i++){
answer[i]=0;
number[i]=numstr[i]-'0';//把char类型数字转化为int类型
}
//初始化的准备
char c[10][10]={
"", //0
"", //1
"abc", //2
"def", //3
"ghi", //4
"jkl", //5
"mno", //6
"pqrs", //7
"tuv", //8
"wxyz" //9
};
int total[10]={0,0,3,3,3,3,3,4,3,4};
int n=TelLength;//n为电话号码的位数
while(1){
int i;
for(i=0;i<TelLength;i++){
printf("%c",c[number[i]][answer[i]]);
}
printf("\n");
int k=n-1;
while(k>=0){
if(answer[k]<total[number[k]]-1){
answer[k]++;
break;
}else{
answer[k]=0;k--;
}
}
if(k<0){
break;
}
}
}
int main() {
getwords("234");
}