#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的过程。

