UVA - 1401:Remember the Word

Remember the Word

来源:UVA

标签:字符串->Trie,动态规划

参考资料:

相似题目:

题目

Neal is very curious about combinatorial problems, and now here comes a problem about words. Knowing that Ray has a photographic memory and this may not trouble him, Neal gives it to Jiejie. Since Jiejie can’t remember numbers clearly, he just uses sticks to help himself. Allowing for Jiejie’s only 20071027 sticks, he can only record the remainders of the numbers divided by total amount of sticks. The problem is as follows: a word needs to be divided into small pieces in such a way that each piece is from some given set of words. Given a word and the set of words, Jiejie should calculate the number of ways the given word can be divided, using the words in the set.

输入

The input file contains multiple test cases. For each test case: the first line contains the given word whose length is no more than 300 000. The second line contains an integer S, 1 ≤ S ≤ 4000. Each of the following S lines contains one word from the set. Each word will be at most 100 characters long. There will be no two identical words and all letters in the words will be lowercase. There is a blank line between consecutive test cases. You should proceed to the end of file.

输出

For each test case, output the number, as described above, from the task description modulo 20071027.

输入样例

abcd
4
a
b
cd
ab

输出样例

Case 1: 2

题目大意

给出一些单词x和一个长字符串s,长字符串长度为L,把s分解成若干个单词x的连接(单词可以重复使用),问分解的方案数有多少?例如:有4个单词a、b、cd、ab,则abcd有2种分解方案:a+b+cd,ab+cd。

解题思路

设dp[i]表示子串s[i…(L-1)]的分解方案数,可以得到如下递推关系式dp[i]=sum{ dp[i+len(x)] },x是给定的单词,且是s[i…(L-1)]的前缀,建议自己尝试理解。例如,以上述为例,dp[0~4]:2,1,1,0,1(初始化dp[L]=1)。
建立Trie树,存入每个单词,然后查找子串的每个前缀是否存在于Trie树中,存在则更新dp数组,详见代码。

参考代码

#include
#include
#define MOD 20071027
#define MAXN 300005
#define sigma_size 26

int ch[MAXN][sigma_size];
int val[MAXN];
int sz;

char str[MAXN];//长字符串 
char dict[105];//单词 
int dp[MAXN];

int idx(char c){
	return c-'a';
}

void insert(char *s, int v){
	int u=0,n=strlen(s);
	for(int i=0;i<n;i++){
		int c=idx(s[i]);
		if(!ch[u][c]){
			memset(ch[sz],0,sizeof(ch[sz]));
			val[sz]=0;
			ch[u][c]=sz++;
		}
		u=ch[u][c];
	}
	val[u]=v; 
}

void init(){
	sz=1;
	memset(ch[0],0,sizeof(ch[0]));
}

int main(){
	int T=0;
	while(~scanf("%s",str)){
		init();	//初始化Trie树 
		int n;
		scanf("%d",&n);
		for(int i=1;i<=n;i++){
			scanf("%s",dict);
			insert(dict,1);//插入单词,且令val=1
		}
		int len=strlen(str);
		memset(dp,0,sizeof(dp));
		dp[len]=1;
		for(int i=len-1;i>=0;i--){
			//查找前缀过程 
			int u=0;
			for(int j=i;j<len;j++){
		        int c=idx(str[j]);
		        if(!ch[u][c]) break;
		        u=ch[u][c];
		        if(val[u]) dp[i]=(dp[i]+dp[j+1])%MOD;//如果有这个前缀,则更新dp数组 
		}
		printf("Case %d: %d\n",++T,dp[0]);
	}
	return 0;
}

你可能感兴趣的:(【记录】算法题解)