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

Posted dwtfukgv

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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;
}

 

以上是关于LA 3942 && UVa 1401 Remember the Word (Trie + DP)的主要内容,如果未能解决你的问题,请参考以下文章

[LA 3942] Remember the Word

BZOJ 3670 && BZOJ 3620 && BZOJ 3942 KMP

LA3942 Remember the Word

UVA-3942 Remember the Word

树状数组 LA 4329 亚洲赛北京赛区题

LA 3942 Remember the word