URAL 1002|Phone Numbers|动态规划

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

题目翻译

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

1 ij    2 abc   3 def
4 gh    5 kl    6 mn
7 prs   8 tuv   9 wxy
        0 oqz

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

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

输入

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

输出

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

样例输入

7325189087
5
it
your
reality
real
our
4294967296
5
it
your
reality
real
our
-1

样例输出

reality our
No 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');
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值