Remember the Word UVALive - 3942(dp+trie)

题意:

  给S个不同的单词和一个长字符串

  问将其分解为若干个单词有多少种方法(单词可重复使用)

解析:

  dp[i]表示在这个字符串中以某个位置i为起点的 的一段子字符串

则这个子字符串若存在某个前缀恰好是字典里出现的 这里把前缀的长度设为len

则dp[i] = dp[i] + dp[i+len+1]

#include <iostream>
#include <cstdio>
#include <sstream>
#include <cstring>
#include <map>
#include <set>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <cmath>
#define rap(i, a, n) for(int i=a; i<=n; i++)
#define rep(i, a, n) for(int i=a; i<n; i++)
#define lap(i, a, n) for(int i=n; i>=a; i--)
#define lep(i, a, n) for(int i=n; i>a; i--)
#define MOD 20071027
#define LL long long
#define ULL unsigned long long
#define Pair pair<int, int>
#define mem(a, b) memset(a, b, sizeof(a))
#define _  ios_base::sync_with_stdio(0),cin.tie(0)
//freopen("1.txt", "r", stdin);
using namespace std;
const int maxn = 300005, INF = 0x7fffffff;
int tot, n, m, rt;
int trie[maxn][26], dp[maxn], vis[maxn];
string s, str;

int build()
{
    int len = s.size();
    rt = 0;
    for(int i=0; i<len; i++)
    {
        int x = s[i] - 'a';
        if(!trie[rt][x])
        {
            trie[rt][x] = ++tot;
        }
        rt = trie[rt][x];
    }
    vis[rt] = 1;
}

void qp(int pos)
{
    int len = str.size();
    rt = 0;
    for(int i=pos; i<len; i++)
    {
        int x = str[i]- 'a';
        if(!trie[rt][x]) return;
        rt = trie[rt][x];
        if(vis[rt])
        {
            dp[pos] = (dp[i+1] + dp[pos]) % MOD;
        }
    }
}

void init()
{
    tot = 0;
    mem(dp, 0);
    mem(vis, 0);
    mem(trie, 0);
}


int main()
{
    int kase = 0;
    while(cin>> str)
    {
        init();
        int n;
        scanf("%d", &n);
        rep(i,0, n)
        {
            cin>> s;
            build();
        }
        int len = str.size();
        dp[len] = 1;
        for(int i=len-1; i>=0; i--) qp(i);
        printf("Case %d: %d\n", ++kase, dp[0]);

    }


    return 0;
}

 

posted @ 2018-08-10 10:43  WTSRUVF  阅读(140)  评论(0编辑  收藏  举报