Timus 1002

#include <iostream>
#include
<string>
#include
<vector>
#include
<algorithm>
using namespace std;

typedef pair
<int, int> word_index;

typedef pair
<int, int> lv1_index;

#define NOT_VALUE 0xffffffff

typedef
struct dp_item {
int min_word_amount;
int min_word_seq[200]; //最长的word序列也就是100个单字母组成
} dp_item;


string words[100010];
dp_item dp[
200] = { 0 }; //号码最长是100个字符
lv1_index index_lv1[100]; //一级索引使用数组,不用vector,因为这样可以更方便地根据 索引项的单词长度,随机访问一级索引,单个词最长也就50个字符

bool cmp_index(const word_index &a, const word_index &b) {

if( a.second == b.second )
return a.first < b.first;

return a.second < b.second;
}

char letter_to_num(char letter) {
switch(letter) {
case 'i': case 'j': return '1';
case 'a': case 'b': case 'c': return '2';
case 'd': case 'e': case 'f': return '3';
case 'g': case 'h': return '4';
case 'k': case 'l': return '5';
case 'm': case 'n': return '6';
case 'p': case 'r': case 's': return '7';
case 't': case 'u': case 'v': return '8';
case 'w': case 'x': case 'y': return '9';
case 'o': case 'q': case 'z': return '0';
}
}

bool match(const string &sub_num, const string &word) {
for( int i=0; i < sub_num.length(); i++ )
if( letter_to_num(word[i]) != sub_num[i] )
return false;

return true;
}

int main() {

string number;
vector
< word_index > sorted_index_lv2, sorted_index_lv1;
vector
< word_index >::iterator it;
int word_amount = 0, i, j, d, k, word_len, number_len, idx;
int min, min_choice, min_choice_word;

cin
>>number;
while(number != "-1") {
memset(dp, NOT_VALUE,
sizeof(dp));
memset(index_lv1, NOT_VALUE,
sizeof(index_lv1));
sorted_index_lv2.clear();

number_len
= number.length();

cin
>>word_amount;

if( word_amount == 0 || number_len == 0 ) {
cout
<<"No solution."<<endl;
cin
>>number;
continue;
}

for( i=0;i<word_amount;i++ )
cin
>>words[i];

for( i=0;i<word_amount;i++ )
sorted_index_lv2.push_back(make_pair(i, (
int)(words[i].length())));

sort(sorted_index_lv2.begin(), sorted_index_lv2.end(), cmp_index);
//完成二级索引

word_len
= sorted_index_lv2[0].second;
index_lv1[word_len]
= make_pair(0, 1);

for( i=1; i<sorted_index_lv2.size(); i++ ) { //构造一级索引
if( sorted_index_lv2[i].second != word_len ) {
word_len
= sorted_index_lv2[i].second; //长度为旧word_len的单词没有了,到新的word_len长度了
index_lv1[word_len] = make_pair(i, 1); //记录第一个长度为word_len的单词在sorted_index_lv2中的位置i
}
else {
index_lv1[word_len].second
++; //找到多一个长度为word_len的单词
}
}

dp[
0].min_word_amount = 0;
for( i=1;i <= number_len;i++ ) {

for( k=0, min_choice = -1; k < i; k++) {
if( dp[k].min_word_amount == NOT_VALUE ) //没有长k的匹配前缀,就算后面有长i-k的匹配后缀,也没用
continue;

idx
= index_lv1[i-k].first; //获取后缀长度的单词组的一级索引

if( idx == NOT_VALUE ) // 没有长度为k的单词
continue;

//遍历所有长度为i的单词
for( j=idx, d=0; d < index_lv1[i-k].second; d++, j++ ) { //second保存的是,在二级索引中,有多少个索引项的单词是长度为i-k的。

if( dp[i].min_word_amount == NOT_VALUE || dp[i].min_word_amount > dp[k].min_word_amount + 1) { //当k=0时,计算对象的就是后缀长度为i的单词

if( match(number.substr(k, i-k), words[sorted_index_lv2[j].first]) ) { //判断长度为i-k的单词是否符合号码的相应部分
dp[i].min_word_amount = dp[k].min_word_amount + 1;
min_choice
= k;
min_choice_word
= sorted_index_lv2[j].first;
break; //同一长度的单词,选第一个能匹配的就够
}
}
}
}

if( dp[i].min_word_amount != NOT_VALUE ) { //长度为i的前缀串拥有最短匹配单词组

if( dp[min_choice].min_word_amount > 0 )
memcpy(dp[i].min_word_seq, dp[min_choice].min_word_seq,
sizeof(int) * dp[min_choice].min_word_amount); //不用担心最初序列长度是0,因为什么都不复制 ^_^

dp[i].min_word_seq[ dp[i].min_word_amount
- 1 ] = min_choice_word;
}
}

if( dp[number_len].min_word_amount == NOT_VALUE ) {
cout
<<"No solution."<<endl;
}
else {
for( i = 0; i < dp[number_len].min_word_amount - 1; i++ )
cout
<<words[ dp[number_len].min_word_seq[i] ]<<" ";
cout
<<words[ dp[number_len].min_word_seq[i] ]<<endl;
}

cin
>>number;
}

return 0;
}

情结的一题,从大一开始就一直想AC,但一直做到大一尾声也做不出来,相隔3年了,昨晚就随便15分钟想出了算法。

DP的情况是比较简单的,只是如何操纵字符串有点难度,通过建立2级的索引,可以用过长度来索引到所有拥有该长度的单词,最后就剩下个DP的过程。

posted @ 2011-08-16 15:16  DOF_KL  阅读(229)  评论(0编辑  收藏  举报