题目:
对如非全键盘的手机上的数字,每个数字都对应一些字母,比如2对应ABC,3对应DEF………,8对应TUV,9对应WXYZ,要求对一段数字,输出其代表的所有可能的字母组合,如5869,可能代表JTMW、JTMX……………..
方法1:(递归DFS)
本题除去说如何判断什么样的单词是一个有意义的单词这个细节不谈(后文中说要先构造一个字典)。那么问题就转化为如何生成所有可能的字符单词。比如输入123,要生成所有的可能的“单词”:#AD,#AE,#AF,#BD,#BE,#BF,#CD,#CE,#CF。
这是一种全排列的输出方式,如果从解答树上就更易看出来了:
23
/ | /
(A B C)
/ | / / | / / | /
(D E F) (DEF) (D E F)
所以最适宜用DFS:
代码:
#include “stdafx.h”
#include<iostream>
#include<stdio.h>
#include<time.h>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
#include<queue>
#include<Windows.h>
using namespace std;
//定义电话号码长度
#define MAX 20
//定义数字对应的字符
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]=
{
1,1,3,3,3,3,3,4,3,4
};
//电话号码
char number[MAX];
int number_len;
int answer[MAX];//存放位置
void print()
{
for(int i=0;i<number_len;i++)
{
cout<<c[number[i]-‘0’][answer[i]];
}
cout<<endl;
}
//level表示循环的层次,号码的位数
//第level个数字
void dfs(int level)
{
//终止情况
if(level==number_len)
{
print();
return ;
}
//该数字是:
int num=number[level]-‘0’;
for(int i=0;i<total[num];i++)
{
//对应answer位置赋值
answer[level]=i;
dfs(level+1);
}
}
int _tmain(int argc, _TCHAR* argv[])
{
while(cin>>number)
{
number_len=strlen(number);
//第0个数字
dfs(0);
}
::system(“pause”);
return 0;
}
方法2:(非递归 神奇的双重while循环)
注:显然可以利用这种方法,打印出任意一个N维数组的任意组合。
我的理解:
这段代码虽然很精妙,但是要构造出来有点困难,并且理解起来也不好理解,有这个时间的话,还不如直接dfs,因为实际在程序的运行都是一样的,没有必要用巧妙来代替代码的速度,除非这个方案很常用,或者高效。让我想起了外星人写的求PI程序。
核心:
参见书本P217。
代码:
#include “stdafx.h”
#include<iostream>
#include<stdio.h>
#include<time.h>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
#include<queue>
#include<Windows.h>
using namespace std;
//定义电话号码长度
#define MAX 20
//定义数字对应的字符
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]=
{
1,1,3,3,3,3,3,4,3,4
};
//电话号码
char number[MAX];
int number_len;
int answer[MAX];//存放位置
int _tmain(int argc, _TCHAR* argv[])
{
while(cin>>number)
{
number_len=strlen(number);
while (true)
{
int k = number_len – 1;
//一次循环一次从第k个键向前移动一位
while (k >= 0)
{
for (int i = 0; i < number_len; i++)
cout << c[number[i]-‘0’][answer[i]] << ” “;
cout << endl;
if (answer[k] < total[number[k]-‘0’] – 1)
{
//第k个键移动
answer[k]++;
break; //这里只要有一个键的一个位置发生一次变化,就退出,打印一次。
}
else
{
//第k个键移动完毕,回到0,开始遍历上一个键。
//此时,继续内部循环。下次循环,处理上一个键。
answer[k] = 0;
k–;
}
}
if (k < 0)
break;
}
}
::system(“pause”);
return 0;
}