题意:给出一个由s个不同的单词组成的字典和一个长字符串str。把这个字符串分解成若干个单词的连接(可重复使用),求有多少种不同的分法。
思路:字典树+dp。用字典树去匹配单词,并dp计数。dp(x)表示从str[x]到末尾的分解方法数。如果str[i]~str[j]正好与某个单词相匹配,那么dp(i)就应该加上dp(j)。字典树的建树主要参考了大白书。
#include <iostream>
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <iomanip>
#include <cstdlib>
#include <string>
#include <memory.h>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <ctype.h>
#define INF 1<<30
#define ll long long
#define max3(a,b,c) max(a,max(b,c))
#define MAXA 100000
using namespace std;
char text[300010];
char word[4010][110];
int dp[600010];
struct Trie{
int ch[300010][26];
int val[300010];
int sz;
void init(){
sz=1;
memset(ch[0],0,sizeof(ch[0]));
}
void insert(char* str,int v){
int u=0;
int len=strlen(str);
for(int i=0;i<len;i++){
if(!ch[u][str[i]-'a']){
memset(ch[sz],0,sizeof(ch[sz]));
val[sz]=0;
ch[u][str[i]-'a']=sz++;
}
u=ch[u][str[i]-'a'];
}
val[u]=v;
}
bool sear(char* str,int s,int len){
int u=0;
for(int i=0;i<len;i++){
if(!ch[u][str[i]-'a'])return 0;
u=ch[u][str[i]-'a'];
if(val[u]){
dp[s]+=dp[s+i+1];
dp[s]%=20071027;
}
}
if(!val[u])return 0;
return 1;
}
};
Trie root;
int main(){
int cas=0;
while(~scanf("%s",text)){
root.init();
cas++;
memset(dp,0,sizeof(dp));
int n;
cin>>n;
for(int i=1;i<=n;i++){
scanf("%s",word[i]);
root.insert(word[i],1);
}
int end=strlen(text);
dp[end]=1;
for(int i=end-1;i>=0;i--){
root.sear(text+i,i,end-i);
}
printf("Case %d: %d\n",cas,dp[0]);
}
return 0;
}