Timus 1002. Phone numbers 要求根据所给的字典找出表示指定电话号码的单词序列。
Problem Source: Central European Olympiad in Informatics 1999
解答如下:
返回目录
1002. Phone numbers
Time Limit: 2.0 second
Memory Limit: 16 MB
Memory Limit: 16 MB
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 4 gh 7 prs |
2 abc 5 kl 8 tuv 0 oqz |
3 def 6 mn 9 wxy |
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 file doesn't exceed 300 KB. The last line of input file 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. Sample
input | output |
---|---|
7325189087 5 it your reality real our 4294967296 5 it your reality real our -1 |
reality our No solution. |
解答如下:
1 using System;
2 using System.IO;
3 using System.Drawing;
4 using System.Collections.Generic;
5
6 namespace Skyiv.Ben.Timus
7 {
8 // http://acm.timus.ru/problem.aspx?space=1&num=1002
9 sealed class T1002
10 {
11 static void Main()
12 {
13 new T1002().Run(Console.In, Console.Out);
14 }
15
16 void Run(TextReader reader, TextWriter writer)
17 {
18 while (true)
19 {
20 string phone = reader.ReadLine();
21 if (phone == "-1") break;
22 string[] words = new string[int.Parse(reader.ReadLine())];
23 string[] numbers = new string[words.Length];
24 for (int i = 0; i < words.Length; i++)
25 numbers[i] = WordToNumber(words[i] = reader.ReadLine());
26 int[] idxs = GetMatchIndexs(phone, numbers);
27 if (idxs == null) writer.Write("No solution.");
28 else foreach (int idx in idxs) writer.Write(words[idx] + " ");
29 writer.WriteLine();
30 }
31 }
32
33 int[] GetMatchIndexs(string text, string[] dict)
34 {
35 bool[] match = new bool[text.Length + 2];
36 match[1] = true;
37 CharacterRange[] list = new CharacterRange[match.Length];
38 for (int i = 1; i < list.Length; i++) list[i].Length = text.Length + 1;
39 for (int i = 1; i <= text.Length; i++)
40 {
41 if (!match[i]) continue;
42 for (int j = 0; j < dict.Length; j++)
43 {
44 int n = dict[j].Length;
45 if (text.Length - i + 1 < n || dict[j] != text.Substring(i - 1, n)) continue;
46 match[i + n] = true;
47 if (list[i + n - 1].Length <= list[i - 1].Length + 1) continue;
48 list[i + n - 1].Length = list[i - 1].Length + 1;
49 list[i + n - 1].First = j;
50 }
51 }
52 int k = text.Length;
53 if (list[k].Length > k) return null;
54 int[] idxs = new int[list[k].Length];
55 for (int i = idxs.Length - 1; i >= 0; k -= dict[idxs[i]].Length, i--)
56 idxs[i] = list[k].First;
57 return idxs;
58 }
59
60 string WordToNumber(string word)
61 {
62 char[] number = new char[word.Length];
63 for (int i = 0; i < word.Length; i++) number[i] = TheDictionary[word[i]];
64 return new string(number);
65 }
66
67 Dictionary<char, char> theDictionary = null;
68
69 Dictionary<char, char> TheDictionary
70 {
71 get
72 {
73 if (theDictionary == null)
74 {
75 theDictionary = new Dictionary<char,char>();
76 for (int i = 0; i < 26; i++)
77 theDictionary.Add((char)('a' + i), "22233344115566070778889990"[i]);
78 }
79 return theDictionary;
80 }
81 }
82 }
83 }
84
2 using System.IO;
3 using System.Drawing;
4 using System.Collections.Generic;
5
6 namespace Skyiv.Ben.Timus
7 {
8 // http://acm.timus.ru/problem.aspx?space=1&num=1002
9 sealed class T1002
10 {
11 static void Main()
12 {
13 new T1002().Run(Console.In, Console.Out);
14 }
15
16 void Run(TextReader reader, TextWriter writer)
17 {
18 while (true)
19 {
20 string phone = reader.ReadLine();
21 if (phone == "-1") break;
22 string[] words = new string[int.Parse(reader.ReadLine())];
23 string[] numbers = new string[words.Length];
24 for (int i = 0; i < words.Length; i++)
25 numbers[i] = WordToNumber(words[i] = reader.ReadLine());
26 int[] idxs = GetMatchIndexs(phone, numbers);
27 if (idxs == null) writer.Write("No solution.");
28 else foreach (int idx in idxs) writer.Write(words[idx] + " ");
29 writer.WriteLine();
30 }
31 }
32
33 int[] GetMatchIndexs(string text, string[] dict)
34 {
35 bool[] match = new bool[text.Length + 2];
36 match[1] = true;
37 CharacterRange[] list = new CharacterRange[match.Length];
38 for (int i = 1; i < list.Length; i++) list[i].Length = text.Length + 1;
39 for (int i = 1; i <= text.Length; i++)
40 {
41 if (!match[i]) continue;
42 for (int j = 0; j < dict.Length; j++)
43 {
44 int n = dict[j].Length;
45 if (text.Length - i + 1 < n || dict[j] != text.Substring(i - 1, n)) continue;
46 match[i + n] = true;
47 if (list[i + n - 1].Length <= list[i - 1].Length + 1) continue;
48 list[i + n - 1].Length = list[i - 1].Length + 1;
49 list[i + n - 1].First = j;
50 }
51 }
52 int k = text.Length;
53 if (list[k].Length > k) return null;
54 int[] idxs = new int[list[k].Length];
55 for (int i = idxs.Length - 1; i >= 0; k -= dict[idxs[i]].Length, i--)
56 idxs[i] = list[k].First;
57 return idxs;
58 }
59
60 string WordToNumber(string word)
61 {
62 char[] number = new char[word.Length];
63 for (int i = 0; i < word.Length; i++) number[i] = TheDictionary[word[i]];
64 return new string(number);
65 }
66
67 Dictionary<char, char> theDictionary = null;
68
69 Dictionary<char, char> TheDictionary
70 {
71 get
72 {
73 if (theDictionary == null)
74 {
75 theDictionary = new Dictionary<char,char>();
76 for (int i = 0; i < 26; i++)
77 theDictionary.Add((char)('a' + i), "22233344115566070778889990"[i]);
78 }
79 return theDictionary;
80 }
81 }
82 }
83 }
84
上述程序 16 - 31 行的 Run 方法循环读取输入,在 25 行调用 WordToNumber 方法(60 - 65 行)将字典中的英文单词转换为阿拉伯数字。然后在 26 行调用 GetMatchIndexs 方法在字典中寻找匹配的英文单词序列。最后在 27 - 29 行输出结果。
这个程序的核心是 33 - 58 行的 GetMatchIndexs 方法。布尔数组 match 表示在该点是否有一个匹配。CharacterRange 数组 list 用来记录匹配的情况,其 Length 属性表示到该点处已经匹配的单词数,First 属性表示在该点处匹配的单词在字典中的索引。该方法在 39 - 51 行遍历要匹配的文本(即电话号码),在每个匹配点遍历所给的字典以寻找下一个匹配点,如果下一个匹配点位于尚未匹配处或者到该点处已经匹配的单词数更大的话,就更新该处的匹配信息。最后,在 52 - 57 行返回匹配的单词序列在字典中的索引,其中第 53 行判断如果最后一个匹配点不在要匹配的文本的结尾则说明匹配失败。
据说 Aho-Corasick 算法也可以用于这道题,且时间复杂度是线性的。
返回目录