UVA 1401 Remember the Word(字典树+dp)

题目链接https://vjudge.net/problem/UVA-1401

参考自:①https://www.cnblogs.com/xuqiulin/p/4034143.html

              ②《算法竞赛入门经典 训练指南》 刘汝佳

解题思路

首先想到dp的思路,dp[i]表示str[0]~str[i-1]的字符串可以分解成的单词的方法数目

那么dp[0] 等于 1 (这个感觉比较玄学,理解为给出每次字典上的单词存在一个为空的串吧)

如果【a,b】出现了一个完整的单词,那么证明  dp[b+1] += dp[a];

这个dp[a]有两种可能:

    ①0,意味着【0,a-1】不能拆成字典中的单词,那么现在就算【a,b】能拆也没用,不能全部拆开,所以dp=0

    ②>0,意味着【0,a-1】可以拆,当前【0,b】可以拆的方法就增加上【0,a-1】可以拆的方法,相当于前面【0,a-1】

有那么多种方法,最后把【a,b】这一块放在最后罢了

 

那么一个dp[i]怎么样才是准确的呢?

以i-1为后缀,【0,i-1】是单词吗?是那么dp[i]+=dp[0],不是就不管

【1,i-1】是单词吗?      同上。

.......

直到【i-1,i-1】

 

最后dp【strlen(str)】就是答案啦。

②dp思路分析清楚了,那么就要解决怎么判断【a,b】是否是字典中的单词了

暴力判断就是一个个单词遍历过去,和每个单词作比较。用py想都知道要超时。

所以用字典树来查单词。

现打的板子(应该没问题,不放心可以对照着这题的代码看看)

int trie[maxnode][sigma_size];///maxnode表示节点数,maxnode=单词数*每个单词最大长度(最坏情况每个单词组合都不一样)
                              ///sigma_size 表示一个节点最多延伸出多少各节点,也就是字符种类
///  trie[i][j] 的值表示 i节点指向j这个数字对应字母 的节点的位置
bool val[maxnode];            ///为真表示当前节点是一个单词的结束
int sz;

int idx(char ch) { return ch-'a';}//将字母转换成对应数字,如果还有大写,标点的话要更麻烦些

void init(int x){
    val[x] = false;
    memset(trie[x],0,sizeof trie[x]);
}

void insert(char *s){///插入
    int u = 0;
    for (int i=0;s[i];i++){
        int v = idx(s[i]);
        if (!trie[u][v]){
            init(sz);///初始化多组输入时上一次残留的数据
            trie[u][v] = sz++;
        }
        u = trie[u][v];
    }
    val[u] = true;
}

bool query(char *s){
    int u = 0;
    for (int i=0;s[i];i++){
        int v = idx(s[i]);
        if (!trie[u][v]) return false; ///这个节点还没有开辟
        u = trie[u][v];
    }
    return true;
}

本题代码

#include<cstdio>
#include<cstring>
#define mod 20071027
using namespace std;

const int maxnode = 4e5+5;
const int N = 3e5+5;
const int sigma_size = 26;

char str[N];
char words[105];
int ch[maxnode][sigma_size],dp[N];
bool val[maxnode];
int sz;

void init(int i) { val[i] = false; memset(ch[i],0,sizeof ch[i]);}

int idx(char c) { return c-'a';}

void insert(char *s){
    int u = 0;
    for (int i=0;s[i];i++){
        int v = idx(s[i]);
        if (!ch[u][v]){
            init(sz); ///将新节点初始化,清除上一组测试数据
            ch[u][v] = sz++;
        }
        u = ch[u][v];
    }
    val[u] = true;
}
/*
bool query(char *s){
    int u = 0;
    for (int i=0;s[i];i++){
        int v = idx(s[i]);
        if (!ch[u][v]) return false;
        u = ch[u][v];
    }
    return true;
}
*/
void DP(){
    memset(dp,0,sizeof dp);
    dp[0] = 1;
    int i;
    for (i=0;str[i];i++){
        int u = 0;
        for (int j = i;str[j];j++){
            int v = idx(str[j]);
            if (!ch[u][v]) break;
            u = ch[u][v];
            if (val[u]) dp[j+1] = (dp[j+1]+dp[i])%mod;
        }
    }
    printf("%d\n",dp[i]);
}

int main()
{
    for (int kca=1;~scanf("%s",str);kca++){
        sz = 1;
        init(0);
        int n;
        scanf("%d",&n);
        while (n--){
            scanf("%s",words);
            insert(words);
        }
        printf("Case %d: ",kca);
        DP();
    }
    return 0;
}

这应该只是字典树入入入入门级操作,更高深的慢慢学吧~~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值