LA 3942 && UVa 1401 Remember the Word (Trie + DP)

题意:给你一个由s个不同单词组成的字典和一个长字符串L,让你把这个长字符串分解成若干个单词连接(单词是可以重复使用的),求有多少种。(算法入门训练指南-P209)

析:我个去,一看这不是一个DP吗?刚开始交一直是runtime error,找了好久,一直以为是数组开小了,不断增大还是这样,后来发现我用了char类型。。。下面分析这个题目

应该不难想到这个状态转移方程:

d(i) = sum{d(i+len(x))|单词x是s[i...L]的前缀},其中len(x)是长度。d(i)表示从字符i开始的字符串(也就是后缀s[i...L])的种数。

很明显我们是从后往前递推的,d(i)的种数应该是由d(i)和d(i+len(x))的和组成的(想想为什么,不理解可以画个图分析一下)。

如果先枚举x,再判断它是不是s[i...L]的前缀,时间复杂度太高了。所以换一个思路,先把单词组成Tire(前缀树),然后试着在Tire中去找s[i..L]。具体参考代码。

代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;
const int maxn = 4000 * 100 + 10; // 4000个单词,每个单词最长是100,最多就有这么多
const int maxm = 300010;
const int mod = 20071027;
int d[maxm];
char ss[maxm], t[110];

struct Tire{
    int ch[maxn][26];
    int val[maxn];
    int sz;
    void init() { sz = 1; memset(val, 0, sizeof(val));  memset(ch[0], 0, sizeof(ch[0])); } //初始化
    int idx(char c){   return c - ‘a‘; } //获得编号

    void inser(char *s){ // 插入
        int u = 0, n = strlen(s);
        for(int i = 0; i < n; ++i){
            int c = idx(s[i]);
            if(!ch[u][c]){
                memset(ch[sz], 0, sizeof(ch[sz]));
                ch[u][c] = sz++;
            }
            u = ch[u][c];
        }
        val[u] = n;
    }

    void quary(char *s, int i, int n){ // 查找
        int u = 0;
        for(int j = 0; j < n; ++j){
            int c = idx(s[j]);
            if(!ch[u][c])  return ;
            u = ch[u][c];
            if(val[u])  d[i] = (d[i] + d[i + val[u]]) % mod;
        }
    }
};
Tire tire;

int main(){
    int n, kase = 0;
    while(~scanf("%s %d", ss, &n)){
        tire.init(); //一定要初始化,刚开始Tire定义在里面,一运行就崩。。。我也是醉了

        for(int i = 0; i < n; ++i){
            scanf("%s", t);
            tire.inser(t);
        }

        memset(d, 0, sizeof(d));
        int len = strlen(ss);
        d[len] = 1;// 这个地方是边界,注意初始化

        for(int i = len-1; i >= 0; --i)
            tire.quary(ss+i, i, len-i); //递推种数
        printf("Case %d: %d\n", ++kase, d[0]);
    }
    return 0;
}
时间: 2024-10-12 03:56:09

LA 3942 && UVa 1401 Remember the Word (Trie + DP)的相关文章

uva 1401 Remember the Word ( Trie + DP )

题意:给出n(n<=4000)个单词和一个字符串(len<=300000),求把这个字符串分解成若干个单词的拼接,有多少种方法 思路:Trie,先把单词建成Trie,然后进行dp,dp[i]表示以字符串中第i个字母为开头的情况,然后每个状态只要在Trie树上找到相应的字母开头的单词,然后dp[i] = sum{dp[i + len(x)]}进行状态转移即可 这个x是n个单词中的一个,可以用Trie查询 code: #include<cstdio> #include<cstri

UVa 1401 Remember the Word

Trie+DP 大白书上的字典树训练题. 题意是说一个字符串可能有多少种小串组成. 例如 abcd 4 a b cd ab abcd=a+b+cd:abcd=ab+cd: 递推为:从最后一位往前,dp[i]=dp[i]+dp[i+ len[x]]  x为输入时的顺序,附加到节点中.是 i~strlen(S)的前缀.S[1,2,3,-,i,-len] 构建Trie树时,把顺序也附加到节点中. 最后search的时候 找出为多少个字符串的前缀,并把这些前缀都加起来. #include<cstdio>

UVA - 1401 Remember the Word(trie+dp)

1.给一个串,在给一个单词集合,求用这个单词集合组成串,共有多少种组法. 例如:串 abcd, 单词集合 a, b, cd, ab 组合方式:2种: a,b,cd ab,cd 2.把单词集合建立字典树,然后从后向前dp,dp[i]=dp[i]+dp[i+len(x)]; 其中x为当前找到的前缀长度. 3. #include<iostream> #include<stdio.h> #include<string.h> using namespace std; #defin

UVALive - 3942 Remember the Word[Trie DP]

UVALive - 3942 Remember the Word Neal is very curious about combinatorial problems, and now here comes a problem about words. Know- ing that Ray has a photographic memory and this may not trouble him, Neal gives it to Jiejie. Since Jiejie can’t remem

LA3942 Remember the Word(Trie+DP)

Trie图的简单应用.这题关键是想出递推式.令d(i)表示从字符i开始的字符串,d(i)=sum{d(i+len(x))},x是s[i...L]的前缀.然后把所有可分解成的单词构造成一颗Trie树,再让母串在上面跑,d[0]即是方案总数. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define mod 20071027 #define M 400005

【UVA1401】Remember the Word Trie+dp

题目大意:给定一个字符串和一个字符串集合,问从集合中选出若干个串组成给定母串的不同方案数. 题解:有些类似于背包问题.状态很好表示,为:\(dp[i]\) 表示母串前 i 个字符的不同方案数,因此,有状态转移方程为:\(dp[i]=\Sigma dp[j],s[j+1...i]=s_0,s_0\in set\) ,可以发现若枚举 \(j < i\) 作为决策集合的话,时间复杂度将是 \(O(n^2)\) 的.优化:可以用 Trie 来直接进行匹配,具体操作如下:将每个集合中的串倒序插入 Trie

LA 3942 Remember the Word (Trie)

Remember the Word 题目:链接 题意:给出一个有S个不同单词组成的字典和一个长字符串.把这个字符串分解成若干个单词的连接(单词可以重复使用),有多少种方法? 思路:令d[i]表示从字符i开始的字符串(后缀s[i..L])的分解数,这d[i] = sum{d(i+len(x)) | 单词x是其前缀}.然后将所有单词建成一个Trie树,就可以将搜索单词的复杂度降低. 代码: #include<map> #include<set> #include<queue>

LA ——3942 - Remember the Word(Trie 入门)

3942 - Remember the Word Regionals 2007 >> Asia - Nanjing Time limit: 3.000 seconds ------------------------------------------------------ 从右往左地推,令dp[i] 表示字符串  S[i....len]的分解方案数,则dp[i]=sum(dp[i+len(x)])  ,我们只要枚举 S[i....len]的前缀,在所给的单词中查找前缀,如果存在,则进行状态

LA 3942 Remember the Word (Trie树)

——刘汝佳的白皮书里面介绍的题目. /* Problem: Status : By WF, */ #include "algorithm" #include "iostream" #include "cstring" #include "cstdio" #include "string" #include "stack" #include "cmath" #inclu