LA 3942 Remember the Word 字典树 DP

题目链接

https://vjudge.net/problem/UVALive-3942

题意

给定一个字符串str和N个单词s。把这个字符串分解成若干个单词的连接(单词可以重复使用),有多少种方法?

解题

题目可以理解为由空串“走”到目标串str。每次走的“步数”恰好是一个单词。比如有单词a、b、cd、ab。目标串为abcd。可以由空串走到a,b,cd,ab。然后可以从a走到aa,ab,acd,aab.等等。
注意到从空串往目标串不太好,考虑从目标串走到空串。
字符串str[i…len]可以由字符串str[j…len]走到,当且仅当str[i…j-1]是一个单词。
而判断str[i…j-1]是否是一个单词,我们可以用str[i…len]去和字典树比较,数下有多少个单词是str[i…len]的前缀。(字典树又叫前缀树)。
设dp[i]表示str[i…len]的分解方法,那么
dp[i]=sigma d[i+len(x)],单词x是str[i…len]的前缀。

AC代码

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

const int maxn=4e3+7;
const int mod=20071027;
int ch[maxn*110][26],sz,ed[maxn*110];
int dp[300010],m;
char str[300010];

void init()
{
    memset(ch[0],0,sizeof(ch[0]));
    sz=0;
}
void insert(char *s)
{
    int len=strlen(s),p=0;
    for(int i=0;i<len;i++)
    {
        int c=s[i]-'a';
        if(!ch[p][c])
        {

            ch[p][c]=++sz;
            ed[sz]=0;
            memset(ch[sz],0,sizeof(ch[sz]));
        }
        p=ch[p][c];
    }
    ed[p]=1;
}
void query(int pos)
{
    int len=m,p=0;
    for(int i=pos;i<len;i++)
    {
        int c=str[i]-'a';
        if(!ch[p][c]) return ;
        p=ch[p][c];
        if(ed[p]) dp[pos]=(dp[pos]+dp[pos+i-pos+1])%mod;
    }
}


int main()
{
    int kase=0;
    while(~scanf("%s",str))
    {
        int n;
        scanf("%d",&n);
        init();
        while(n--)
        {
            char s[110];
            scanf("%s",s);
            insert(s);
        }

        memset(dp,0,sizeof(dp));
        m=strlen(str);
        dp[m]=1;
        for(int i=m-1;i>=0;i--)
            query(i);
       // for(int i=0;i<=m;i++)
          //  printf("%d %d\n",i,dp[i]);
        printf("Case %d: %d\n",++kase,dp[0]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值