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 your favourite teacher is read BULLDOG.
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 50 000). 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 doesn't exceed 300 KB. The last line contains call number −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.
Link:http://acm.timus.ru/problem.aspx?space=1&num=1002
题目大意:每个字母有对应的数字,那么,为了方便电话号码的记忆,我们可以把这些数字换成多个单词来方便记忆。现在给出一串数字,和多个单词,要求用最少的单词来表示出所有的数字,有多个答案任意给出一个即可。
思路:乍眼看去好像有点小难……仔细想想,首先,因为每个字母都对应着一个数字,可以把这些字母都替换成数字,这样就变成了数字与数字之间的匹配。
然后,题目可以解释为:给你多个字符串(只包含数字),用最少的字符串组成一个特定的字符串。
于是,我们可以发现每个字符串所能覆盖的位置都是固定的,用KMP算法可以把他们都找出来。
这个是不是有点像最短路呢?对于每一个字符串,若它能覆盖的位置是从 i 到 j,那么连一条边i→j+1。
从0~n做最短路,选什么算法随意,当然既然是个DAG,顺序还是固定的,直接DP式地做这个最短路即可。
然后递归输出答案,解决。
代码(0.031S):
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #include <iostream> 5 using namespace std; 6 7 const int MAXV = 110; 8 const int MAXS = 50010; 9 const int MAXL = 55; 10 const int MAXE = MAXS * MAXL; 11 12 int number[26] = {2, 2, 2, 3, 3, 3, 4, 4, 1, 1, 5, 5, 6, 6, 0, 7, 0, 7, 7, 8, 8, 8, 9, 9, 9, 0}; 13 int mat[MAXV][MAXV], pre[MAXV], ecnt, n, k; 14 int s[MAXS][MAXL], src[MAXV], len[MAXS]; 15 char ans[MAXS][MAXL], str[MAXV]; 16 17 void init() { 18 memset(mat, -1, sizeof(mat)); 19 } 20 21 void add_edge(int u, int v, int pos) { 22 mat[u][v] = pos; 23 } 24 25 int dis[MAXV]; 26 27 bool SPFA() { 28 memset(dis, 0x3f, sizeof(dis)); 29 dis[0] = 0; 30 for(int i = 0; i < n; ++i) { 31 for(int j = i + 1; j <= n; ++j) { 32 if(~mat[i][j] && dis[i] + 1 < dis[j]) { 33 pre[j] = mat[i][j]; 34 dis[j] = dis[i] + 1; 35 } 36 } 37 } 38 return dis[n] <= n; 39 } 40 41 void getFail(int P[], int m, int f[]) { 42 f[0] = f[1] = 0; 43 for(int i = 1; i < m; ++i) { 44 int j = f[i]; 45 while(j && P[i] != P[j]) j = f[j]; 46 f[i + 1] = (P[i] == P[j] ? j + 1 : 0); 47 } 48 } 49 50 void KMP(int T[], int n, int P[], int m, int f[], int pos) { 51 getFail(P, m, f); 52 for(int i = 0, j = 0; i < n; ++i) { 53 while(j && P[j] != T[i]) j = f[j]; 54 if(P[j] == T[i]) ++j; 55 if(j == m) add_edge(i - m + 1, i + 1, pos); 56 } 57 } 58 59 void print(int pos) { 60 if(pos - len[pre[pos]] != 0) { 61 print(pos - len[pre[pos]]); 62 putchar(' '); 63 } 64 printf("%s", ans[pre[pos]]); 65 } 66 67 int fail[MAXL]; 68 69 int main() { 70 while(scanf("%s", str) != EOF) { 71 if(strcmp(str, "-1") == 0) break; 72 n = strlen(str); 73 for(int i = 0; i < n; ++i) src[i] = str[i] - '0'; 74 scanf("%d", &k); 75 for(int i = 0; i < k; ++i) { 76 scanf("%s", ans[i]); 77 len[i] = strlen(ans[i]); 78 for(int j = 0; j < len[i]; ++j) s[i][j] = number[ans[i][j] - 'a']; 79 } 80 init(); 81 for(int i = 0; i < k; ++i) 82 KMP(src, n, s[i], len[i], fail, i); 83 if(SPFA()) print(n), puts(""); 84 else puts("No solution."); 85 } 86 }