URAL 1002|Phone Numbers|动态规划

来源:互联网 发布:最新网络术语 编辑:程序博客网 时间:2024/05/17 07:45

http://acm.timus.ru/problem.aspx?space=1&num=1002

题目翻译

现代社会中,你常常会遇到一大堆电话号码,而且这些号码正在变得越来越长,你却需要记住这些号码。一种简易的解决方法是建立数字与字母的对应关系,比如下面这种:

1 ij    2 abc   3 def4 gh    5 kl    6 mn7 prs   8 tuv   9 wxy        0 oqz

这样一个数字关联一些字母,这样你可以通过记单词的方式记忆号码。显然如果能找到号码与单词之间的简单的联系是坠吼的。比如你的一个棋友的电话号码是941837296,你可以读作WHITEPAWN,你最喜欢的老师的电话号码是2855304,则可以读作BULLDOG。

你需要写一个程序,找出单词数目最少的一组单词,其可以表示对应的电话号码。对应关系如上。

输入

输入包含多组测试数据。对于每组数据,第一行电话号码,第二行一个正整数n(n50,000),表示字典里有多少个单词供你记忆。接下来n行,每行一个字符串,表示可以组成句子的单词,单词最多50个字符,均为小写英文字母。输入文件不超过300KB。输入文件最后一行-1表示输入结束。

输出

对于每组数据,输出一行,如果存在这样的一种表示方式,则输出对应的句子,如果存在多种句子符合条件,输出单词数目最少的那个,如果还存在多种句子符合条件,随便输出一种。如果不存在,输出”No solution.”。

样例输入

73251890875ityourrealityrealour42949672965ityourrealityrealour-1

样例输出

reality ourNo solution.

题解

dpi表示电话号码前i位可以用一个句子表示的话,最少的单词数目。
那么显然有

dpi=minj{dpilenj+1}(mappedwordj=callilenj+1..i)

其中mapped_word表示单词对应的号码是多少。

#include <cstdio>#include <cstring>#define FOR(i,j,k) for(i=j;i<=k;++i)#define rep(i,j,k) for(i=j;i<k;++i)const char map[] = "22233344115566070778889990";char s[128], word[65536][64];int len[65536], dp[128], sel[128];void output(int n) {    if (n == 0) return;    else output(n - len[sel[n]]);    printf("%s ", word[sel[n]] + 1);}int main() {    int i, j, k, n, l;    while (scanf("%s", s + 1) != EOF && s[1] != '-') {        scanf("%d", &n); l = strlen(s + 1);        memset(dp, 127, sizeof dp);        dp[0] = 0;        FOR(i,1,n) scanf("%s", word[i] + 1), len[i] = strlen(word[i] + 1);        FOR(j,1,l) FOR(i,1,n) if (j >= len[i]) {            bool flag = true;            FOR(k,1,len[i]) if (s[j - len[i] + k] != map[word[i][k] - 'a']) {                flag = false;                break;            }            if (!flag) continue;            if (dp[j] > dp[j - len[i]] + 1) {                dp[j] = dp[j - len[i]] + 1;                sel[j] = i;            }        }        if (dp[l] == 0x7f7f7f7f) puts("No solution.");        else output(l), putchar('\n');    }}