分词
Time limit per test: 1.0 seconds
Time limit all tests: 1.0 seconds
Memory limit: 256 megabytes
有一句句子因为粘贴的时候出现了一点问题空格全部丢失了。现在给一本字典,每个词都对应这个词出现的频率(每十亿)。根据这个频率,我们可以根据下面的公式算出这个词带来的收益 P(word) :
其中 frequency 就是上面所提到的频率。 len 指的是单词的长度。
特别的,对于字典中没有出现过的词, P(word)=0 。
请对句子进行适当的分割,使得分割得到的所有词收益之和最大。同一个词可以重复出现,收益算作多次。
Input
先给出一本词典,词典的第一行是词条数(词条数约为 40 000 ),下面每行分别是单词和对应出现频率,用空格隔开。单词中只会出现英文字母大小写,不会有多余的空格。每个单词只会出现一次。频率是一个正实数。
所有单词长度之和不超过 3⋅105 ,最长单词长度不超过 30 。
接下来一行一个整数 T (T≤10) ,表示有 T 个查询。
下面 T 行,每行一个句子,句子长度不超过 5 000 。句子中保证只含有英文字母大小写。注意单词匹配时,不区分大小写。
词典数据来源于 Wikipedia Project Gutenberg(可能需要代理),其中 1-10000 词汇。
查询数据来源于 IELTS Test。
Output
对于每组数据,输出两行。
第一行是一个实数,表示最大能达到的收益。输出和答案相差不超过 10−3 即可认为正确。
第二行输出一连串单词,单词和单词之间用空格隔开。满足:
- 把这些单词依次串联起来可以得到原句子;
- 所有单词的收益值相加得到第一行的实数。
Examples
5 ano 10 ther 30 another 10 an 300 other 20 1 another
112.826670 another
5 ano 10.0 ther 30.0 another 10.0 an 300.0 other 2000.0 1 another
212.837691 an other
区间DP可能会超时;
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5+10;
double dp[N], val[N];
int tre[N][30], a[N];
char str[N];
void dfs(int k)
{
if(!k) return ;
dfs(a[k]);
if(a[k]) printf(" ");
for(int i=a[k]+1;i<=k;i++) printf("%c",str[i]);
return ;
}
int main()
{
int n;
while(scanf("%d", &n)!=EOF)
{
int cnt=0;//地址
val[0]=0;
memset(tre[0],0,sizeof(tre[0]));
for(int i=0; i<n; i++)
{
scanf("%s",str+1);
int len=strlen(str+1), k=0;
for(int j=1; j<=len; j++)
{
int x=str[j]>='a'?(str[j]-'a'):(str[j]-'A');
if(!tre[k][x])
{
tre[k][x]=++cnt;
memset(tre[cnt],0,sizeof(tre[cnt]));
val[cnt]=0;
}
k=tre[k][x];
}
scanf("%lf",&val[k]);
val[k]=1.0*len*len*log(1.0*val[k]);
}
int t;
scanf("%d", &t);
while(t--)
{
memset(dp,0,sizeof(dp));
scanf("%s",str+1);
int len=strlen(str+1);
memset(a,0,sizeof(a));
for(int i=1;i<=len;i++)
{
int k=0;
for(int j=0;j<35;j++)
{
if(i+j>len) break;
int x=str[i+j]>='a'?(str[i+j]-'a'):(str[i+j]-'A');
if(tre[k][x])
{
k=tre[k][x];
if(dp[i+j]<dp[i-1]+val[k])
{
dp[i+j]=dp[i-1]+val[k];
a[i+j]=i-1;
}
}
else break;
}
}
printf("%lf\n",dp[len]);
dfs(len);
printf("\n");
}
}
return 0;
}