字典树,$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;
}