问题描述:
对如非全键盘的手机上的数字,每个数字都对应一些字母,比如2对应ABC,3对应DEF.........,8对应TUV,9对应WXYZ,要求对一段数字,输出其代表的所有可能的字母组合,如5869,可能代表JTMW、JTMX.................
思路:
将各个数字所能代表的字符存储在一个二维数组c[][]中,其中假设0、1所代表的字符为空字符。见下面代码:
const char c[10][10]={//存储各个数字所能代表的字符
"", //0
"", //1
"ABC", //2
"DEF", //3
"GHI", //4
"JKL", //5
"MNO", //6
"PQRS", //7
"TUV", //8
"WXYZ", //9
};
const int total[10]={0,0,3,3,3,3,3,4,3,4};//存储各个数字所能代表的字符总数
/*
用一个数组存储电话号码int number[TelLength];//TelLength为电话号码的位数
将数字目前所能表达的位置用answer[TelLength];
于是对于电话号码第一位c[number[0]][answer[0]]=c[4][2]='I';
*/
解法一:直接循环法
见代码:如果多加一个数字,就得多加一个位置循环以遍历该数字对应的所有字符
void printWord(int number[],int numLen)
{
int *answer = new int[numLen];
for(answer[0]=0 ; answer[0]<total[number[0]] ; answer[0]++)//遍历三个数字对应的所有字符
for(answer[1]=0 ; answer[1]<total[number[1]] ; answer[1]++)
for(answer[2]=0 ; answer[2]<total[number[2]] ; answer[2]++){
for(int i=0 ; i<numLen ; i++)//遍历所有数字
cout<<c[number[i]][answer[i]];
cout<<endl;
}
delete []answer;
}
下面有一个直接循环法的改良版,免去了多加循环的烦恼,每个数字对应字符位置变化一次就跳出循环输出一次。按照这个思路,下面是代码:
//直接循环法改良
//个人觉得这种方法很难想到
void printWord(int number[],int numLen)
{
int *answer = new int [numLen];
memset(answer,0,sizeof(int)*numLen);
while (true)
{
for(int i=0 ; i<numLen ; i++)
cout<<c[number[i]][answer[i]];
cout<<endl;
int k = numLen-1;//当回滚至上一个数字answer[k-1]++后返回大循环,又开始从最后一位向前回滚。体现了递归的思想
while(k>=0){
if(answer[k] < total[number[k]]-1){
answer[k]++;//每变动一下就出去输出一次,直到answer[k]变到最后一个位置
break;
}
else{//answer[k]到尾部,则清零本数字对应字符位置,并回滚至上一个数字对应字符位置
answer[k]=0;
k--;
}
}
if(k<0)
break;
}
delete[]answer;
}
解法二:递归解法
每层的for循环其实可以看成是一个递归函数的调用。代码如下:
//递归解法
//index说明对电话号码的第几位进行循环
void printWord(int number[],int answer[] , int index , int numLen)
{
if(index == numLen){//叶子节点处输出所有情况
for(int i=0 ; i<numLen ; i++)//遍历所有号码
cout<<c[number[i]][answer[i]];
cout<<endl;
return;
}
for(answer[index]=0 ; answer[index]<total[number[index]] ; answer[index]++)//每改变本层字母一次都需要改变接下来所有情况
printWord(number,answer,index+1,numLen);
}