• 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
    ", ++kase, d[0]);
        }
        return 0;
    }
    
  • 相关阅读:
    [BZOJ2212][POI2011]Tree Rotations(线段树合并)
    [BZOJ3569]DZY Loves Chinese II(随机化+线性基)
    [BZOJ3237][AHOI2013]连通图(分治并查集)
    [BZOJ4945][NOI2017]游戏(2-SAT)
    [BZOJ4568][SCOI2016]幸运数字(倍增LCA,点分治+线性基)
    [BZOJ2460][BJOI2011]元素(线性基)
    [BZOJ4942][NOI2017]整数(线段树+压位)
    [P2023][AHOI2009]维护序列(线段树)
    [HDU4336]Card Collector(min-max容斥,最值反演)
    [COGS2426][HZOI 2016]几何
  • 原文地址:https://www.cnblogs.com/dwtfukgv/p/5527295.html
Copyright © 2020-2023  润新知