soj 3596 Article Decryption(trie树 + dp)

@(K ACMer)

题意:
给你一些单词,和一个长字符串s,s是由这些单词组成的,总共有多少种组成的可能?
分析:
问有多少种可能显然的dp,很容易的想到定义 dp[i] 为s前i个字符形成的字符串最多有多少种可能,则有转移方程:

dp[i]=j=0i1isword(j+1,i) ? dp[j] : 0
(:isword(j+1,i)sj+1i)

这里在实现isword()的时候如果用map去查找,复杂度是 O(nl) ,显然不好,就需要建一颗trie树, O(l) 的去查是否为单词.
总的复杂度是 (O(ln2+ml)) ,其中n为s的长度,m为单词的个数 ml 的复杂度是建立trie树的过程中产生的.

code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <map>
#include <stack>
#include <vector>
#include <string>
#include <queue>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
const int mod = 835672545, INF = 0x3fffffff, maxn = 1e5 + 40;
char str[maxn];
int dp[maxn];

#define MAX 26

typedef struct trienode
{
    int num;  // 该节点前缀 出现的次数
    struct trienode *next[MAX]; //该节点的后续节点
} trienode;

trienode memory[100000]; //先分配好内存。 malloc 较为费时
int mcnt = 0; // 内存计数器

//初始化一个节点。nCount计数为1, next都为null
trienode* create(void)
{
    trienode* tmp = &memory[mcnt++];  //节点指向一个已经分配好的内存节点之一.
    tmp -> num = 0;  //当前节点初始化个数为1
    for (int i = 0; i < MAX; i++)  //当前节点的所有儿子的指针初始化为null
        tmp->next[i] = NULL;
    return tmp;  //返回分配好的当前节点的指针
}

void insert(trienode* tmp, char* str)   //把字符串加入tire树中
{
    int i = 0, k;
    while (str[i]) {  //一个一个的插入字符
        k = str[i++] - 'a'; //当前字符 应该插入的位置
        if (tmp -> next[k] );  //如果当前节点的该儿子已经存在,就把儿子的频率加1
        else
            tmp -> next[k] = create();  //如果该节点的该儿子不存在,就创建一个
        tmp = tmp -> next[k]; // 树往高深度进展
        if (str[i] == '\0') tmp -> num++;
    }
}

int search(trienode* root, char * str)  //查找str为前缀的字符串,在树中的个数
{
    if (root == NULL)  //如果根节点为空,返回0
        return 0;
    trienode* tmp = root;  //当前节点
    int i = 0, k;
    while (str[i]) { //一直查找完整个字符串放可罢休
        k = str[i++] - 'a';  //当前节点对应的儿子的位置
        if (tmp -> next[k])  //如果儿子存在,就递归到儿子
            tmp = tmp->next[k];
        else
            return 0;  //前缀已经查完了,退出
    }
    return tmp -> num; //返回最后的那个字符,所在节点的nCount(也就是这个字符串在原串中的数量)
}

bool judge(int i, int j, trienode* rt) {
    char* p = str + i;
    char c = str[j + 1];
    str[j + 1] = '\0';
    int ok = search(rt, p);
    str[j + 1] = c;
    return ok == 0 ? false : true;
}

int main(void)
{
    int T;
    scanf("%d", &T);
    while (T--) {
        trienode* rt = create();
        int n;
        scanf("%d", &n);
        while (n--) {
            scanf("%s", str);
            insert(rt, str);
        }
        str[0] = 'a';
        scanf("%s", str + 1);
        int slen = strlen(str) - 1;
        memset(dp, 0, sizeof(dp));
        dp[0] = 1;
        for (int i = 1; i <= slen; i++) {
            for (int j = i - 1; j >= 0; j--) {
                if (judge(j + 1, i, rt))
                    dp[i] = (dp[i] % mod + dp[j] % mod) % mod;
            }
        }
        printf("%d\n", dp[slen]);
    }

    return 0;
}

你可能感兴趣的:(dp)