UVALive

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

题意:给出一个字典和一个字符串,将这个字符串分解成若干个单词的连接(单词可以重复使用),问有多少种分法

dp[i]表示以i开始(即后缀[i,L])的字符串的分解方案数

dp[i]=Σ dp[i+len[x]] (x是[i,L]的前缀)

判断x是否是[i,L]的前缀:

构造字典树,

如果在i发现了单次结尾,dp[]+=dp[i+1],不需要字典树每个节点挂一个链表

#include<cstdio>
#include<cstring>

#define N 300001
#define M 101
#define mod 20071027
using namespace std;

char s[N],a[M];
int m,len;
int trie[M*4001][27],tot;
int dp[N];
int mark[M*4001];

void insert(int j)
{
    len=strlen(a);
    int root=0,id;
    for(int i=0;i<len;i++)
    {
        id=a[i]-'a';
        if(!trie[root][id]) trie[root][id]=++tot;
        root=trie[root][id];
    }
    mark[root]=j;
}
void find(int start,int en)
{
    int root=0,id,ans=0;
    for(int i=start;i<=en;i++)
    {
        id=s[i]-'a';
        if(!trie[root][id]) break ;
        root=trie[root][id];
        if(mark[root]) 
        {
            ans+=dp[i+1]%mod; 
            ans%=mod;
        }
    }
    dp[start]=ans;
}
int main()
{
    int t=0;
    while(scanf("%s",s)!=EOF)
    {

        scanf("%d",&m);
        memset(dp,0,sizeof(dp));
        memset(mark,0,sizeof(mark));
        memset(trie,0,sizeof(trie));
        for(int i=1;i<=m;i++)
        {
            scanf("%s",a);
            insert(i);
        }
        int l=strlen(s);
        dp[l]=1;
        for(int i=l-1;i>=0;i--)  find(i,l-1);
        printf("Case %d: %d
",++t,dp[0]);
    }
}
原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/6957130.html