描述
题解
一看这个题,我第一感觉就是 dp,想着可能是 map + 区间 dp,但是发现数据太大,开不来二维 dp,有些手足无措,真情太少,套路太深,后来搞懂原来这个是字典树 + dp,但是不是简单的区间 dp,具体我也不知道这叫啥 dp,反正挺有趣的一个 dp,具体代码里很容易看懂,就是不容易想……
这个题用 map + dp 应该是可解的,不过涉及到字符串切割的问题稍微麻烦一些,也可能效率没有直接字典树搞来的快,毕竟我们需要不断用子串去匹配。
哎,dp 博大精深,需要好好学习啦,几个月前刷过 dp 专题,现在已经忘却了太多,智商不够用了,忘性太大了~~~
代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
const int MAXN = 3e5 + 10;
const int MAX_WORD_LEN = 30;
int flag[MAXN];
char s[MAXN];
int Trie[MAXN][26], sz;
double dp[MAXN];
double val[MAXN];
void format(int x)
{
if (!x)
{
return ;
}
format(flag[x]);
if (flag[x])
{
printf(" ");
}
for (int i = flag[x] + 1; i <= x; i++)
{
printf("%c", s[i]);
}
}
int main()
{
int N, T;
while (cin >> N)
{
sz = 0;
val[0] = 0;
memset(Trie[0], 0, sizeof(Trie[0]));
for (int i = 0; i < N; i++)
{
scanf("%s", s);
int k = 0;
int len = (int)strlen(s);
for (int j = 0; j < len; j++)
{
int x = s[j] - (s[j] >= 'a' ? 'a' : 'A');
if (!Trie[k][x])
{
Trie[k][x] = ++sz;
memset(Trie[sz], 0, sizeof(Trie[sz]));
val[sz] = 0;
}
k = Trie[k][x];
}
scanf("%lf", &val[k]);
val[k] = log(val[k]) * len * len;
}
cin >> T;
for (int i = 0; i < T; i++)
{
scanf("%s", s + 1);
memset(dp, 0, sizeof(dp));
int len = (int)strlen(s + 1);
for (int j = 1; j <= len; j++)
{
int rt = 0;
for (int k = 0; k < MAX_WORD_LEN; k++)
{
if (j + k > len)
{
break;
}
int x = s[j + k] - (s[j + k] >= 'a' ? 'a' : 'A');
if (Trie[rt][x])
{
rt = Trie[rt][x];
if (dp[j + k] < dp[j - 1] + val[rt])
{
dp[j + k] = dp[j - 1] + val[rt];
flag[j + k] = j - 1;
}
}
else
{
break;
}
}
}
printf("%lf\n", dp[len]);
format(len);
puts("");
}
}
return 0;
}