uvalive 3942 Remember the Word (字典树+DP)

给S个不同的单词和一个长字符串,问将其分解为若干个单词有多少种方法(单词可重复使用)


解法:

设dp[i]表示以i开头的字符串分解的方法数。状态转移方程:dp[i]=sum(dp[i+len(x)]),x为S[i……L]的前缀。

将每一个单词插入到字典树中,然后考虑长字符串S的每一个后缀s[i……L], 看其前缀x是否在字典树中出现,每出现一次,dp[i]累加dp[i+len(x)]即可。


注意总结点数目大小,以及dp的初始化。


#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
#define maxn 300005
#define maxm 26
#define mod 20071027
int tree[maxn][maxm],val[maxn],cnt;//Trie树、结点信息、结点数目
int dp[300005];
void init()
{
    cnt=1;
    memset(tree[0],0,sizeof(tree[0]));
    memset(dp,0,sizeof(dp));
}

inline void Insert(char *s,int v)
{
    int id=0,l=strlen(s);
    for(int i=0; i<l; ++i)
    {
        int c=s[i]-'a';
        if(!tree[id][c])
        {
            memset(tree[cnt],0,sizeof(tree[cnt]));
            tree[id][c]=cnt++;
            val[cnt]=0;
        }
        id=tree[id][c];
    }
    val[id]=v;//字符串的最后一个字符对应结点的附加信息为v
}


inline void querry(char *s,int l,int pos)
{
    int id=0;
    for(int i=pos; i<l; ++i)
    {
        int c=s[i]-'a';
        if(!tree[id][c]) return;
        id=tree[id][c];
        if(val[id]) dp[pos]=(dp[pos]+dp[i+1])%mod;
    }
}

char str[300005],w[105];
int main()
{
    int n,ca=1;
    while(~scanf("%s",str))
    {
        scanf("%d",&n);
        init();
        for(int i=0; i<n; ++i)
        {
            scanf("%s",w);
            Insert(w,1);
        }
        int l=strlen(str);
        dp[l]=1;
        for(int i=l-1;i>=0;--i) querry(str,l,i);
        printf("Case %d: %d\n",ca++,dp[0]);
    }
    return 0;
}


你可能感兴趣的:(uvalive 3942 Remember the Word (字典树+DP))