您的位置:首页 > 其它

UVALive 3942 Remember the Word(字典树+dp)

2015-08-03 09:00 330 查看

题意:

给定一个长度不超过30000的字符串SS,然后给定n(n<=4000)个长度不超过100的字符串aia_i,问用aia_i组合成SS有多少种方案数,由于数量可能很大,最终结果mod 20071027。

题解:

本来想学习一下字典树的,但是这题用到了dp。

dp[i]dp[i]表示从ii开始到SS末尾有多少种组合方案。

那么dp公式为:dp[i]=∑dp[i+len(x)]dp[i]=\sum dp[i+len(x)] (x表示已经存在的字符串)。

所以我们可以从后往前递推,枚举dp[i]dp[i],然后再枚举中断点判断[i,j][i,j]的单词是否出现过,最后dp[0]dp[0]就是最终答案。


但是如果直接暴力,会超时,所以我们需要用字典树保存aia_i,每次查找单词是否出现就在字典树中查询,字典树查询的复杂的就是O(h),h为字符串的长度。


字典树:

向字典那样讲字符串保存在树当中,其中ch[i][j]表示在i状态下,输入字符j时,下个状态的位置是多少(类似于邻接表);val[i]表示i状态的时候是否是一个字符串的终点(也可以记录在这个位置终结的字符串的个数)。


其中ch[i][j]中j范围为字符集范围,例如小写字母是26个,0<=j<26;i比较大,最坏的情况下为len(str)∗max{len(ai)}len(str)*max\{len(a_i)\},这样很可能超出内存,不过这种情况基本不会出现,如果内存不够可以开更小的数组或者改用指针形式的字典树。

mymy codecode

[code]#pragma comment(linker, "/STACK:102400002,102400000")
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;

const int MOD = 20071027;
const int MAXN = 4005 * 100 + 100;

int ch[MAXN][26];
int val[MAXN];
int sz;

struct Trie {

    Trie() {sz = 1; memset(ch[0], 0, sizeof(ch)); }
    int idx(char c) { return c - 'a'; }

    void insert(char *s, int v = 1) {
        int u = 0;
        while(*s) {
            int c = idx(*s);
            if(!ch[u][c]) {
                memset(ch[sz], 0, sizeof(ch[sz]));
                val[sz] = 0;
                ch[u][c] = sz++;
            }
            u = ch[u][c];
            *s++;
        }
        val[u] = v;
    }

    bool find(char* s, int len) {
        int u = 0;
        for(int i = 0; i <= len; i++) {
            int c = idx(s[i]);
            if(!ch[u][c])
                return false; 
            u = ch[u][c];
        }
        return val[u];
    }
};

char s[300005], word[105];
ll dp[300005];
int n;

int main() {
    int cas = 1;
    while(scanf("%s", s) != EOF) {
        Trie trie;
        scanf("%d", &n);
        for(int i = 0; i < n; i++) {
            scanf("%s", word);
            trie.insert(word);
        }

        memset(dp, 0, sizeof(dp));
        int len = strlen(s);
        dp[len] = 1;
        for(int i = len-1; i >= 0; i--) {
            for(int j = 0; j < 100 && i + j < len; j++) {
                if(trie.find(s + i, j)) {
                    dp[i] = (dp[i+j+1] + dp[i]) % MOD;
                }
            }
        }
        printf("Case %d: %lld\n", cas++, dp[0]);
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: