题意:给一个字符串,求组成这个字符串的期望最大,字串有自己的权值。
题解:
考虑动态规划。从一个点出发可以检查后面每一个可能的单词的权值,然后更新它的后继状态。可以用 map 或者 trie 来维护(map 可能需要适当优化,否则很容易超时)。有几个小技巧:
- 最后回溯的时候,只要在每一个走过的地方打一个点,然后从左往右一个一个字符输出,碰到点就输个空格就好了。
- 使用 map 的注意优化:单词长度不超过 30。这样的话超过 30 的单词直接认为权值是 0 就好了。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<string>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
int n,m,cnt;
char str[5010];
double value[200010],dp[200010];
int g[200010][30],pre[200010];
void Insert()
{
int k=0;scanf("%s",str);
int len=strlen(str);
for(int i=0;i<len;i++)
{
int c=str[i]-(str[i]>='a'?'a':'A');
if(!g[k][c])
{g[k][c]=++cnt;value[cnt]=0;memset(g[cnt],0,sizeof(g[cnt]));}
k=g[k][c];
}
scanf("%lf",&value[k]);value[k]=len*len*log(value[k]);
}
void print(int len)
{
if(len==0)return;
print(pre[len]);
if(pre[len])printf(" ");
for(int i=pre[len]+1;i<=len;i++)printf("%c",str[i]);
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
int cnt=0;value[0]=0;
memset(g[0],0,sizeof(g[0]));
for(int i=1;i<=n;i++)
Insert();
scanf("%d",&m);
while(m--)
{
memset(dp,0,sizeof(dp));
scanf("%s",str+1);
int len=strlen(str+1);
for(int i=1;i<=len;i++)
{
int rt=0;
for(int j=0;j<30;j++)
{
if(!str[i+j])continue;
int c=str[i+j]-(str[i+j]>='a'?'a':'A');
if(!g[rt][c])
{
if(dp[i+j]<dp[i-1])
{
dp[i+j]=dp[i-1];
pre[i+j]=i-1;
}
break;
}
else
{
rt=g[rt][c];
if(dp[i+j]<dp[i-1]+value[rt])
{
dp[i+j]=dp[i-1]+value[rt];
pre[i+j]=i-1;
}
}
}
}
printf("%lf\n",dp[len]);print(len);printf("\n");
}
}
return 0;
}