题目描述:给你一个由S个不同单词组成的字典和一个长字符串。把这个字符串分解成若干个单词连接()单词可以重复使用),求有多少种方法。
Sol: 递推
dp[i]=sigma_sum (dp[i+len(x)])
dp[i]表示从字符i开始的字符串即后缀(s[i..L])的分解方案数。
x为是 (s[i..L]的前缀)。
先把所有单词放在trie上,然后在trie上查找S[i....L],没经过一个节点就找到了一个X。。。然后就可以解了。
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int MaxNode = 4000 * 100 + 10;
const int sigma_size = 26;
const int maxL = 300000 + 10;
const int maxWL = 100 + 10;
const int MOD = 20071027;
int dp[maxL],N;
char text[maxL],word[maxWL];
struct Trie{
int ch[MaxNode][sigma_size];
int val[MaxNode];
int sz;//结点总数
void clear() {sz=1;val[0]=0;memset(ch[0],0,sizeof(ch[0]));}
int idx(char c) {return c-'a';}
void insert(const char *s){
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]=n;
}
void find(const char *s,int len,int q){
int u=0;
for(int i=0;i<len;i++){
if(s[i]=='\0') break;
int c=idx(s[i]);
if(!ch[u][c]) break;
u=ch[u][c];
if(val[u]!=0) dp[q]=(dp[q]+dp[q+val[u]])%MOD;
}
}
};
Trie trie;
int main()
{
int Case=0;
while(~scanf("%s%d",text,&N)){
trie.clear();
for(int i=0;i<N;i++){
scanf("%s",word);
trie.insert(word);
}
memset(dp,0,sizeof(dp));
int L=strlen(text);
dp[L]=1;
for(int i=L-1;i>=0;i--){
trie.find(text+i,L-i,i);
}
printf("Case %d: %d\n",++Case,dp[0]);
}
return 0;
}