题目:有一个长串,和一组单词组成的字典,问可以将长串完全拆分成单词的方法数。
分析:字典树,动态规划(dp)。利用字典树做存储和查询,dp求解。
状态定义:f(i)为串str[1..i]的拆分情况,为了方便计算这里长串从下标1开始;
转移方程:f(i)= sum(f(j)),f(j)是str[j+1..i]存在字典中是,前缀的数量;
说明:好久没写题了,UVa上不去。
#include <cstdlib>
#include <cstring>
#include <cstdio>
/* Tire define */
#define nodesize 200002 //节点个数
#define dictsize 26 //字符集大小
typedef struct _tire_node
{
int flag; //值域
_tire_node* next[dictsize];
}tire_node;
tire_node dict[nodesize+1];
tire_node* Q[nodesize+1];
int ID[256];
int dp[300003];
class Tire
{
private:
int size;
tire_node* root;
public:
Tire() {
makeID();
init();
}
void makeID() {
for (int i = 0; i < 26; ++ i) {
ID['a'+i] = i;
}
}
void init() {
memset(dict, 0, sizeof(dict));
root = NULL;
size = 0;
root = newnode();
}
tire_node* newnode() {
return &dict[size ++];
}
void insert(char* word) {
tire_node* now = root;
for (int i = 0; i < word[i]; ++ i) {
if (!now->next[ID[word[i]]]) {
now->next[ID[word[i]]] = newnode();
}
now = now->next[ID[word[i]]];
}
now->flag = 1;
}
int query(char* segment, int s, int e) {
tire_node *now = root;
for (int i = s; i <= e; ++ i) {
if (now->next[ID[segment[i]]]) {
now = now->next[ID[segment[i]]];
}else {
return 0;
}
}
return now->flag;
}
}tire;
/* Tire end */
char text[300003];
char word[101];
int main()
{
int n, t = 1;
while (~scanf("%s", &text[1])) {
scanf("%d", &n);
tire.init();
for (int i = 0; i < n; ++ i) {
scanf("%s",word);
tire.insert(word);
}
memset(dp, 0, sizeof(dp));
dp[0] = 1;
for (int i = 1; text[i]; ++ i) {
dp[i] = 0;
for (int j = i; j > 0 && i-j < 100; -- j) {
if (tire.query(text, j, i)) {
dp[i] = (dp[i] + dp[j-1]) % 20071027;
}
}
}
printf("Case %d: %d\n", t ++, dp[strlen(&text[1])]);
if (getchar() == EOF) {
break;
}
}
return 0;
}