题面
7-11 2K. 破忒头的匿名信
破忒头想要写一封匿名信来做坏事,由于他不想被认出自己的笔迹,因此他想要雇佣萨博来帮他写这封信。萨博按照这样的标准来收费:他的词典里有N个单词,第i个单词的单价是pi。如果你提供一个长度为M的序列,,那么你需要支付∑i=1Mpai的金钱,而萨博会依次往信里写上。破忒头希望支付最少的金钱,让萨博写的内容恰好为他想要的信件内容T。请你告诉破忒头,最少需要付多少钱,能让萨博写出他想要的匿名信,或者告诉他这是不可能做到的。
输入格式:
对于每组测试数据,第一行包含一个正整数N,表示萨博词典里的单词个数(1)。接下来的N行描述词典里的每个单词,第i行包含一个字符串Si和这个字符串的单价pi(1)。接下来一行包含破忒头想要的信件内容T(1)。保证Si和T都仅包含小写字母,且Si的总长度不超过5。
输出格式
如果有可能让萨博写出破忒头想要的信件内容,那么输出一个正整数,表示最小需要付出的代价。否则,输出-1。
输入样例:
4
ab 5
cd 10
abc 100
d 1
abcd
1
ab 1
abc
输出样例:
15
-1
作者: 2020,Winter,Day2
单位: 东北大学秦皇岛分校
时间限制: 5000 ms
内存限制: 256 MB
代码长度限制: 16 KB
题解
基本的ac自动机构造 + DAG上dp
注意insert时,不像以往,对于重复字串,我们只更新其价格使其价格最低,即每个结尾至多只有一个字符串结尾,并记录下这个字符串的长度(深度)。
对于query,不再查询有多少个字串,直接开始dp,有点像01背包,但有所不同。
其他都是细节。
代码如下
#include<cstdio> #include<cstring> #include<algorithm> #define RE register #define FOR(i,a,b) for(RE int i=a;i<=b;++i) #define ROF(i,a,b) for(int i=a;i>=b;--i) #define ll long long #define sc(x) scanf("%d",&x) #define ss(s) scanf("%s",s) using namespace std; const int MAXN = 5e5 + 10; const int inf = 0x3f3f3f3f; const int C = 'a'; const int M = 26; int n, len, cost; char s[MAXN]; ll d[MAXN]; struct ac { int trie[MAXN][M], fail[MAXN], lenth[MAXN], vis[MAXN]; ll val[MAXN]; int q[MAXN], tot; void insert(char* s, ll v) { int p = 0; for (RE int i = 0; s[i]; ++i) { int ch = s[i] - C; if (!trie[p][ch])trie[p][ch] = ++tot; p = trie[p][ch]; lenth[p] = i + 1; } if (val[p])val[p] = min(val[p], v); else val[p] = v; } void build() { int head = 0, tail = -1; FOR(i, 0, M - 1) if (trie[0][i]) q[++tail] = trie[0][i]; while (head <= tail) { int p = q[head++]; FOR(i, 0, M - 1) if (trie[p][i]) fail[trie[p][i]] = trie[fail[p]][i], q[++tail] = trie[p][i]; else trie[p][i] = trie[fail[p]][i]; } } void query(char* s) { int p = 0; for (RE int i = 0; s[i]; ++i) { p = trie[p][s[i] - C]; for (RE int tmp = p; tmp; tmp = fail[tmp]) { if (vis[i - lenth[tmp] + 1] && val[tmp]) { d[i + 1] = min(d[i + 1], d[i - lenth[tmp] + 1] + val[tmp]); vis[i + 1] = 1; } } } } }a; int main() { sc(n); FOR(i, 1, n) { ss(s); sc(cost); a.insert(s, cost); } a.build();ss(s); len = strlen(s); a.vis[0] = 1; for (RE int i = 1; i <= len; ++i) d[i] = 1ll * inf * inf; a.query(s); if (!a.vis[len])puts("-1"); else printf("%lld ", d[len]); return 0; }