Phone numbers
Time Limit: 2 second
Memory Limit: 16M
Background
In the present world you frequently meet a lot of call numbers and they are going to be longer and longer. You need to remember such a kind of numbers. One method to do it in an easy way is to assign letters to digits as shown in the following picture:
现实生活中,你时常会遇到许多许多而且越来越长的电话号码。你需要记住这类型的号码。例如按下面的图示,把字母划分到特定的数字上,是一种很容易就能把数字记住的方法:
1 ij 2 abc 3 def
4 gh 5 kl 6 mn
7 prs 8 tuv 9 wxy
0 oqz
This way every word or a group of words can be assigned a unique number, so you can remember words instead of call numbers. It is evident that it has its own charm if it is possible to find some simple relationship between the word and the person itself. So you can learn that the call number 941837296 of a chess playing friend of yours can be read as WHITEPAWN, and the call number 2855304 of you* **vourite teacher is read BULLDOG.
按这种方法:每个字或一个词组可被代替成一组特定的数字,那么,你只可以通过记住一些词就能记住相应电话号码。如果可以找出一种单词与个人电话号码的简单关系,它是很有吸引力的。例如你的一个棋友的电话号码是941837296,你可以用 WHITEPAWN来代替;又如你可以用BUULDOG来代替你的一个喜爱的老师的电话号码:2855304。
Problem
Write a program to find the shortest sequence of words (i.e. one having the smallest possible number of words) which corresponds to a given number and a given list of words. The correspondence is described by the picture above.
对给定的数字和单词表,求出一个最简短的单词序列(也就是得出一尽可能少的单词来代替相应的数字)。这种对应关系要求符合上图所描述的关系。
Input
Input contains a series of tests. The first line of each test contains the call number, the transcription of which you have to find. The number consists of at most 100 digits. The second line contains the total number of the words in the dictionary (maximum is 50000). Each of the remaining lines contains one word, which consists of maximally 50 small letters of the English alphabet. The total size of the input file doesn't exceed 300KB. The last line of input file contains call number -1.
输入包含若干组的测试数据。每组测试点的第一行是你所要记住的电话号码。这个号码最多有100个数位。测试的第二行是单词总数(最大为50000个)。以下的每一行是只包含一个单词,单词长度最大限制为50个字母。整个输入文件的大小不超过300KB。输入文件的最后一行以-1作为结束标志。
Output
Each line of output contains the shortest sequence of words which has been found by your program. The words are separated by single spaces. If there is no solution to the input data, the line contains text `No solution.'. If there are more solutions having the minimum number of words, you can choose any single one of them.
输出文件的每一行为找到的最短单词序列。每个单词间用一个空格隔开。如果没有解决方案,则输出“No solution.”。如果有多个单词满足条件,可以从中选择任一个单词输出。
Sample Input
7325189087
5
it
your
reality
real
our
4294967296
5
it
your
reality
real
our
-1
Sample Output
reality our
No solution.
解题思路(动态规划)
我们先把问题简单化一些,现在我们只考虑“对于给定的电话号码,至少需要多少个单词才能把它表示出来”,我们用arrLeastWords[i]存放表示电话号码的前i个(注意,如说前2个,则表示的是第0,1,2三个数,因为我用的是c语言,不是pascal,下同)数字至少需要的单词数,如果电话号码的长度为length,则现在我们要求的就是arrLeastWords[length-1]。
对于任何一个arrLeastWords[i](i=0,1,2,……,length-1)应该怎么求解呢?
我们这么来想:
当我们求arrLeastWords[i]时,假设我们已经知道对于给定电话号码的前k个数字至少需要arrLeastWords[k]个单词才能把它表示出来(k = 0,1,2,……,i-1 时均知道),那么对于arrLeastWords[i],就好像在已经处理了的前i-1个号码的后面增加了一个数字,我们要处理新增的这个数字为它找到最优解,当一个新的数字加入进来后会产生三种情况,第一种情况是:它将作为一个单词的末尾数字,且仅用这个单词就可以表示电话号码中前i个数字;第二种情况是:它将作为一个单词的末尾数字(包括这个单词只有一个字母的情况),但这个单词前面还有其它的单词;第三种情况是:加入第i个数字后,将无法找到用来表示前i个数字的单词链.
我们知道,第一种情况是最理想的,第二种情况是在第一种不成立时做出的一种选择,第三种情况是我们不想看到的,因此,对于每一个i我们先假设第三种情况成立,当我们发现存在第一种或第二种情况时我们再来否定它。
所以,我们的程序也是先寻找是否存在第一种情况,为此我们在给定的单词表中查看是否有这么一个单词可以用来表示前i个数字,如果有的话则arrLeastWords[i]等于1,此时我们应该把精力转移到arrLeastWords[i+1]上;若第一种情况不存在,则我们考虑第二种。为此,我们使k从0开始循环到i-1,当电话号码中的第k+1个数字倒第i个数字可以用一个单词来表示且arrLeastWords[k]+1小于当前的arrLeastWords[i]时,我们就找到了一种更优解,我们把此时的arrLeastWords[k]+1存放在arrLeastWords[i]中,直至k=i时循环结束。
若当k=i时arrLeastWords[i]的值自始至终没有发生变化,则我们遇到了第三种情况,此时让我们忽略它,因为一开始我们就假设这种情况是成立的。
正如我们前面所假设的那样----“假设我们已经知道对于给定电话号码的前k个数字至少需要arrLeastWords[k]个单词才能把它表示出来(k = 0,1,2,……,i-1 时均知道)”,我们应该从arrLeastWords[0]开始求起,然后是arrLeastWords[1],arrLeastWords[2],……,arrLeastWords[length-1]。当我们求出arrLeastWords[length-1]时答案也就出来了,若arrLeastWords[length-1]的值为p,则表示对于给定的电话号码至少需要p个单词才能匹配,若arrLeastWords[length-1]的值为我们给定的一个初始值,则对于给定的号码无解。
现在我们来考虑当有解时怎么把解输出来,即哪些单词构成了匹配号码的单词链。
对于任何一个i,当我们求出arrLeastWords[i]时我们也同时知道第i个数字是在哪个单词的末尾,我们用vecWordList[i]存放这个单词,如果这个单词的长度为3,则我们也知道前一个单词就存放在vecWordList[i-3]中,因此我们可以根据vecWordList中保存的单词的长度从vecWordList[length-1]开始反序遍历vecWordList找出所有需要的单词,然后顺序输出。