EOJ-3261 字典树 + dp

题意:

题目链接:http://acm.ecnu.edu.cn/problem/3261/
给出一个词典,包括每个单词的频率,根据公式计算出单词的价值。现在给出一串字符串,要求将字符串分成若干单词,使得分割之后单词价值之后最大,并输出分割结果。


思路:

词典用字典树维护,对于每个字符串进行O(n^2)的dp,dp[j]=dp[i]+val[u],然后路径标记,输出结果即可。


代码:

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 5005;
const int MAXNODE = 3e5 + 10;
const int SIGMA_SIZE = 30;

struct Trie {
    int ch[MAXNODE][SIGMA_SIZE];
    double val[MAXNODE];
    int sz;

    void init() {
        sz = 1;
        memset(ch, 0, sizeof(ch));
    }

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

    void Insert(char *s, double v) {
        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]));
                val[sz] = 0;
                ch[u][c] = sz++;
            }
            u = ch[u][c];
        }
        val[u] = v;
    }

} trie;

char str[MAXN], ss[MAXN];
int pa[MAXN];
double dp[MAXN];

void show(int x) {
    if (x == 0) return;
    show(pa[x]);
    if (pa[x] != 0) printf(" ");
    for (int i = pa[x] + 1; i <= x; i++)
        printf("%c", str[i]);
}

int main() {
    //freopen("in.txt", "r", stdin);
    int n, m;
    scanf("%d", &n);
    trie.init();
    for (int i = 1; i <= n; i++) {
        double x;
        scanf("%s%lf", str, &x);
        int len = strlen(str);
        for (int j = 0; j < len; j++)
            str[j] = tolower(str[j]);
        double v = log(x) * len * len;
        trie.Insert(str, v);
        //cout << str << "  " << v <<endl;
    }
    scanf("%d", &m);
    while (m--) {
        scanf("%s", str + 1);
        int len = strlen(str +1);
        for (int i = 1; i <= len; i++) {
            ss[i] = tolower(str[i]);
        }
        memset(dp, 0, sizeof(dp));
        memset(pa, 0, sizeof(pa));
        for (int i = 0; i < len; i++) {
            int u = 0, j = i + 1;
            for (; j <= len; j++) {
                int c = trie.idx(ss[j]);
                u = trie.ch[u][c];
                if (dp[j] < dp[i] + trie.val[u]) {
                    pa[j] = i;
                    dp[j] = dp[i] + trie.val[u];
                }
                if (u == 0) break;
            }
        }
        printf("%.6f\n", dp[len]);
        show(len);
        puts("");
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值