EOJ#3261 分词

单点时限: 2.0 sec
内存限制: 256 MB
粘贴的时候出现了一点问题空格全部丢失了。现在给一本字典,每个词都对应这个词出现的频率(每十亿)。根据这个频率,我们可以根据下面的公式算出这个词带来的收益 P(word):

P(word)=len2(word)⋅ln(frequency(word))

其中 frequency 就是上面所提到的频率。len 指的是单词的长度。
特别的,对于字典中没有出现过的词,P(word)=0。
请对句子进行适当的分割,使得分割得到的所有词收益之和最大。同一个词可以重复出现,收益算作多次。
输入格式
先给出一本词典,词典的第一行是词条数(词条数约为 40 000),下面每行分别是单词和对应出现频率,用空格隔开。单词中只会出现英文字母大小写,不会有多余的空格。每个单词只会出现一次。频率是一个正实数。
所有单词长度之和不超过 3⋅105,最长单词长度不超过 30。
接下来一行一个整数 T
(T≤10),表示有 T 个查询。
下面 T 行,每行一个句子,句子长度不超过 5 000。句子中保证只含有英文字母大小写。注意单词匹配时,不区分大小写。
词典数据来源于 Wikipedia Project Gutenberg(可能需要代理),其中 1-10000 词汇。
查询数据来源于 IELTS Test。
输出格式
对于每组数据,输出两行。
第一行是一个实数,表示最大能达到的收益。输出和答案相差不超过 10−3 即可认为正确。
第二行输出一连串单词,单词和单词之间用空格隔开。满足:
把这些单词依次串联起来可以得到原句子;
所有单词的收益值相加得到第一行的实数。

看了好多题解,好像都是用字典树/AC自动机搞的,那么在这里贴一个对新人友好一点的方法吧。
dp+hash
先处理一下输入数据,求出hash值、长度、价值三个参量,然后插入到哈希表/map中(个人习惯手写hash表,可以使用一些骚操作
然后对于每一个读入的句子,跑一个dp,dp[i]表示以i为最后一个单词的结尾,对1~i这个句子进行划分所能得到的最大收益。
dp[i]可以从两个地方转移而来,第一个是把最后一个单词看成是没有出现过的单词——价值为0,即dp[i]=max{dp[j]}(0<=j<i),最大值很轻松就可以求出来
另一个是与词库中的单词发生匹配,因为单词最长是30,所以一个O(30)左右的内部循环就可以了。
最后,因为要输出分词方式,所以建立一个pre数组,在每一次更新答案之后,同时更新pre数组,输出的时候递归即可

最后献上代码
(原来代码是有缩进的,复制上来就没有了,我也懒得改······)

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define p 131
#define M 100007
#define ull unsigned long long
#define rt long long
#define inf 1000000000
using namespace std;
inline rt read()
{
   
 register rt u=0,v=1;register char ch=getchar();
 while(!isdigit(ch)){
   if(ch=='-')v=-1;ch=getchar();}
 while(isdigit(ch)){
   u=u*10+ch-'0';ch=getchar
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值