UVALive - 3942 Remember the Word 字典樹+dp

題目鏈接https://cn.vjudge.net/problem/UVALive-3942

題意:

給出一個由n個不同單詞組成的字典和一個長字符串s。把這個字符串分解成若干個單詞的連接(單詞可以重複使用),有多少種方法? 比如,有4個單詞 a,b,ac,cd, 則abcd有兩種分解方法: a+b+cd和ab+cd

s的長度<300000 , 1<=n<=4000, 每個單詞長度<=100

題解:

用dp[i]表示從字符i開始的字符串的分解方案數,dp[i]=sum(dp[i+len(x)])  單詞x是s[i...L]的前綴。

如果枚舉單詞,每次需要枚舉4000多次,如果建一個字典樹,因爲每個字符串長度最長是100,所以在字典樹上跑,每次最多跑100次。   

題目多組輸入,注意初始化。

代碼:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3e5+5;
const int N=4000;
const int mod=20071027;
char c[N];
char s[maxn];
int son[N*100][26],tot;
bool cnt[N*100];
int dp[maxn];
void insert(char* c){
    int len=strlen(c);
    int p=0;  //p爲結點在數組裏的下標
    for(int i=0;i<len;i++){
        int x=c[i]-'a';
        if(son[p][x]==0) son[p][x]=++tot;
        p=son[p][x];
    }
    cnt[p]=true;
}
int search(char *c,int pos){
    int len=strlen(c);
    int p=0;
    int ans=0;
    for(int i=0;i<len;i++){
        int x=c[i]-'a';
        if(son[p][x]==0) break;
        p=son[p][x];
        if(cnt[p]) ans=(ans+dp[pos+i+1])%mod;
    }
    return ans;
}
void init(){
    memset(cnt,0,sizeof cnt);
    memset(son,0,sizeof son);
    tot=0;
}
int main(){
    int cnt=1;
    while(scanf("%s",s)!=EOF){
        init();
        int n;
        scanf("%d",&n);
        while(n--){
            scanf("%s",c);
            insert(c);
        }
        n=strlen(s);
        dp[n]=1;
        for(int i=n-1;i>=0;i--){
            dp[i]=search(s+i,i);
        }
        printf("Case %d: %d\n",cnt++,dp[0]);
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章