1. 程式人生 > >UVALive 3942 Remember the Word

UVALive 3942 Remember the Word

stream 初始 方案 方法 trie 組合 pan mes bcd

題意:給出一個由S個不同單詞組成的字典和一個長字符串。把這個字符串分解成若幹個單詞的連接(單詞可以重復

    使用),有多少種方法?

Sample Input

abcd

4

a

b

cd

ab

Sample Output

Case 1: 2

思路:利用字典樹上的dp,dp[i]表示從i到末尾有多少種方案,大白書字典樹的例題,雖然是例題還是看了一會兒orz,

    我太弱了,註意ch數組要開成最大節點數*26的大小,表示每個節點連的字母是什麽,初始節點為0,val也要開

    成最大節點數的大小,表示該節點是否為單詞的最後一個字母。

    然後就是dp的過程了,從末尾往前,每次循環從i到結尾,如果碰到某個單詞的結尾(也就是val[j]==1)就代表

    該單詞可以作為某些組合的一部分,dp[i]+=dp[j+1],關鍵部分就是這裏了吧。

代碼:

#include<iostream>
#include<string.h>
using namespace std;

const int maxn=4001*101;
const int maxm=3e5+5;
const int mod=20071027;

struct Trie{
int ch[maxn][26];
int val[maxn];
int sz;
Trie(){
sz=1;
memset(ch[0],0,sizeof(ch[0]));
}
int idx(char c){
return c-‘a‘;
}
void insert(char *s,int v){
int u=0,l=strlen(s);
for(int i=0;i<l;i++){
int x=idx(s[i]);
if(ch[u][x]==0){
memset(ch[sz],0,sizeof(ch[sz]));
val[sz]=0;
ch[u][x]=sz++;
}
u=ch[u][x];
}
val[u]=v;
}
}e;

char s[maxm];
char a[110];
long long dp[maxm];

int main(){
int t=0;
while(~scanf("%s",s)){
int n;
scanf("%d",&n);
memset(e.ch[0],0,sizeof(e.ch[0]));
e.sz=1;
for(int i=0;i<n;i++){
scanf("%s",a);
e.insert(a,1);
}
int l=strlen(s);
memset(dp,0,sizeof(dp));
dp[l]=1;
int u;
for(int i=l-1;i>=0;i--){
u=0;
for(int j=i;j<l;j++){
int x=e.idx(s[j]);
if(e.ch[u][x]==0)break;
u=e.ch[u][x];
if(e.val[u])dp[i]+=dp[j+1];
}
dp[i]%=mod;
}
//printf("%d\n",e.sz);
//for(int i=0;i<=l;i++)printf("%lld ",dp[i]);
printf("Case %d: %lld\n",++t,dp[0]);
}
return 0;
}

UVALive 3942 Remember the Word