Uvalive 3942 - Remember the Word(字典树+DP)

题目链接 https://cn.vjudge.net/problem/UVALive-3942

【题意】
给出一个由S个不同单词组成的字典和一个长字符串,把这个长字符串分解成若干个单词的连接(单词可重复使用),有多少种方法?比如有4个单词a,b,cd,ab那么abcd有两种分法a+b+cd以及ab+cd.

【输入格式】
多组数据,每组数据的第一行为长度不超过300000的长字符串,第二行是单词的个数S(1<=S<=4000),以下S行每行一个长度不超过100的单词

【输出格式】
对于每组数据,输出方案数对20171027的余数

【思路】
开始还是没思路,看了大白的讲解才写出来的,设dp(i)表示原字符串的子串[i,len-1]的划分方案数,那么dp(i)=sum{dp(i+len(x)) | x是某个单词,同时x为[i,len-1]的前缀 },边界是dp(len)=1,但是这样做要求枚举所有的4000个单词,同时还要判断是否为前缀,很可能超时。如果我们换一种思路,用所有的单词构造一个Trie,就可以避免枚举所有单词的过程了,因为可以直接从Trie树的根结点开始往下找,找到一个单词就进行相应的状态转移,因为单词的长度不超过100,所以最多找100次就能完成该次计算,下面的代码用的是记忆化搜素求解

#include<bits/stdc++.h>
using namespace std;

const int mod=20071027;
const int maxn=400050;

char p[maxn],s[105];
int trie[maxn][26],tot;
int deep[maxn];
bool isword[maxn];
int dp[maxn];
int num,len;

void init(){
    len=strlen(p);
    tot=0;
    memset(trie,0,sizeof(trie));
    memset(isword,0,sizeof(isword));
    memset(deep,0,sizeof(deep));
    memset(dp,-1,sizeof(dp));
}

int dfs(int i){
    if(i==len) return 1;
    if(dp[i]!=-1) return dp[i];
    int node=0,ans=0;
    for(int j=i;j<len;++j){
        int id=p[j]-'a';
        node=trie[node][id];
        if(isword[node]) ans=(ans+dfs(i+deep[node]))%mod;
        if(!node) break;
    }
    return dp[i]=ans;
}

void insert(){
    int node=0;
    for(int i=0;s[i];++i){
        int id=s[i]-'a';
        if(!trie[node][id]) {
            trie[node][id]=++tot;
            deep[trie[node][id]]=deep[node]+1;
        }
        node=trie[node][id];
    }
    isword[node]=true;
}

int main(){
    int kase=0;
    while(scanf("%s",p)==1){
        init();
        scanf("%d",&num);
        for(int i=0;i<num;++i){
            scanf("%s",s);
            insert();
        }
        int ans=dfs(0);
        printf("Case %d: %d\n",++kase,ans);
    }
    return 0;
}

转载于:https://www.cnblogs.com/wafish/p/10465306.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值