2017华东师范大学网赛-分词

分词

Time limit per test: 1.0 seconds

Time limit all tests: 1.0 seconds

Memory limit: 256 megabytes

有一句句子因为粘贴的时候出现了一点问题空格全部丢失了。现在给一本字典,每个词都对应这个词出现的频率(每十亿)。根据这个频率,我们可以根据下面的公式算出这个词带来的收益  P(word)

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

其中  frequency  就是上面所提到的频率。 len  指的是单词的长度。

特别的,对于字典中没有出现过的词, P(word)=0

请对句子进行适当的分割,使得分割得到的所有词收益之和最大。同一个词可以重复出现,收益算作多次。

Input

先给出一本词典,词典的第一行是词条数(词条数约为  40 000 ),下面每行分别是单词和对应出现频率,用空格隔开。单词中只会出现英文字母大小写,不会有多余的空格。每个单词只会出现一次。频率是一个正实数

所有单词长度之和不超过  3105 ,最长单词长度不超过  30

接下来一行一个整数  T   (T10) ,表示有  T  个查询。

下面  T  行,每行一个句子,句子长度不超过  5 000 。句子中保证只含有英文字母大小写。注意单词匹配时,不区分大小写

词典数据来源于 Wikipedia Project Gutenberg(可能需要代理),其中 1-10000 词汇。

查询数据来源于 IELTS Test。

Output

对于每组数据,输出两行。

第一行是一个实数,表示最大能达到的收益。输出和答案相差不超过  103  即可认为正确。

第二行输出一连串单词,单词和单词之间用空格隔开。满足:

  • 把这些单词依次串联起来可以得到原句子;
  • 所有单词的收益值相加得到第一行的实数。

Examples

input
5
ano 10
ther 30
another 10
an 300
other 20
1
another
output
112.826670
another
input
5
ano 10.0
ther 30.0
another 10.0
an 300.0
other 2000.0
1
another
output
212.837691
an other

Note

样例给出的词典与测试数据有所不同。

Source

2017 华东师范大学网赛
解题思路:字典树+dp


#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <cmath>
#include <map>
#include <bitset>
#include <set>
#include <vector>
#include <functional>

using namespace std;

#define LL long long
const int INF = 0x3f3f3f3f;

int n, t, f[300090][26], p[5005], visit[5005];
double g[300090], dp[5090], m;
char ch[5005], a[50];

int main()
{
	while (~scanf("%d", &n))
	{
		memset(f[0], 0, sizeof f[0]);
		int cnt = 0;
		for (int i = 1; i <= n; i++)
		{
			scanf("%s%lf", a, &m);
			int k = 0, len = strlen(a);
			for (int j = len - 1; j >= 0; j--)
			{
				int x = a[j] >= 'a'&&a[j] <= 'z' ? a[j] - 'a' : a[j] - 'A';
				if (!f[k][x])
				{
					f[k][x] = ++cnt;
					memset(f[cnt], 0, sizeof f[cnt]);
					g[cnt] = 0;
				}
				k = f[k][x];
			}
			g[k] = max(g[k], m);
		}
		scanf("%d", &t);
		while (t--)
		{
			scanf("%s", ch + 1);
			int len = strlen(ch + 1);
			for (int i = 0; i <= len; i++) dp[i] = 0;
			memset(p, -1, sizeof p);
			for (int i = 1; i <= len; i++)
			{
				int k = 0, j;
				for (j = i; j >= 1; j--)
				{
					int x = ch[j] >= 'a'&&ch[j] <= 'z' ? ch[j] - 'a' : ch[j] - 'A';
					if (f[k][x])
					{
						k = f[k][x];
						double xx = g[k] ? log(g[k]) : 0;
						if (dp[i] < dp[j - 1] + (i - j + 1)*(i - j + 1)*xx)
							dp[i] = dp[j - 1] + (i - j + 1)*(i - j + 1)*xx, p[i] = j - 1;
					}
					else break;
				}
				while (j >= 1)
				{
					if (dp[i] < dp[j - 1])
						dp[i] = dp[j - 1], p[i] = j - 1;
					j--;
				}
			}
			printf("%.6lf\n", dp[len]);
			memset(visit, 0, sizeof visit);
			int k = len;
			while (k != -1) { visit[k] = 1, k = p[k]; }
			for (int i = 1; i <= len; i++)
			{
				if (visit[i] && i != len) printf("%c ", ch[i]);
				else printf("%c", ch[i]);
			}
			printf("\n");
		}
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值