首页 > 代码库 > EOJ 3261 分词

EOJ 3261 分词

字典树,$dp$。

记录$dp[i]$为以$i$为结尾获得的最大价值。枚举结尾一段是哪个单词,更新最大值。可以将字典中单词倒着建一棵字典树。

这题数据有点不严谨。

下面这组数据答案应该是负的。

3

a 0.1

aa 0.1

aaa 0.1

1

aaa

下面这组数据没通过的代码在$OJ$上也可以$AC$......

3

a 2

aa 2

aaa 2

1

aaab

正确答案是:

6.238325

aaa b

#include <cstdio>#include <cmath>#include <cstring>#include <algorithm>using namespace std;double eps = 1e-7;struct X{	double f;	int nx[26];}s[300010];int sz=0,root=0;int n,T;char t[5010];char tt[5010];double w;double dp[5010];int pre[5010];int r[5010];void Insert(){	int p = root;	int len = strlen(t);	for(int i=len-1 ; i>=0 ;i--)	{		if(t[i]>=‘A‘&&t[i]<=‘Z‘) t[i] = t[i] -‘A‘ + ‘a‘;		if(s[p].nx[t[i]-‘a‘]==-1) s[p].nx[t[i]-‘a‘] = ++sz;		p = s[p].nx[t[i]-‘a‘];	}	s[p].f = w;}double get(int x){	if(x<0) return 0.0;	return dp[x];}double work(double x){	if(x==0.0) return 0.0;	return log(x);}int main(){	scanf("%d",&n);	for(int i=0;i<=300005;i++)	{		s[i].f = 0;		for(int j=0;j<=25;j++) s[i].nx[j] = -1;	}		sz=0;	for(int i=1;i<=n;i++)	{		scanf("%s%lf",t,&w);		Insert();	}	scanf("%d",&T);	while(T--)	{		scanf("%s",t); tt[0]=0; strcpy(tt,t);		int len = strlen(t);		for(int i=0;i<len;i++) 		{			if(t[i]>=‘A‘&&t[i]<=‘Z‘)				t[i] = t[i] - ‘A‘ +‘a‘;		}		memset(dp,0,sizeof dp);		memset(pre,-1,sizeof pre);		for(int i=0;i<len;i++)		{			int now = i, p = root;			while(1)			{				if(now<0) break;				if(s[p].nx[t[now]-‘a‘]==-1) 				{					for(int e=0;e<now;e++)					{						if(dp[i] < dp[e])						{							dp[i] = dp[e];							pre[i] = e;						}					}					break;				}				p = s[p].nx[t[now]-‘a‘];				if(get(now-1) + work(s[p].f)*(i-now+1)*(i-now+1) > dp[i])				{					dp[i] = get(now-1) + work(s[p].f)*(i-now+1)*(i-now+1);					pre[i] = now-1;				}								now--;			}		}		printf("%.6f\n",dp[len-1]);		memset(r,0,sizeof r);		int pp = pre[len-1];		while(1)		{			if(pp<0) break;			r[pp]=1;			pp = pre[pp];		}		for(int i=0;i<len;i++)		{			printf("%c",tt[i]);			if(r[i]) printf(" ");		}		printf("\n");	}	return 0;}

EOJ 3261 分词