CF633C Spy Syndrome 2
说是 d p dp dp 但是感觉说是一个记忆化也可以。我们定义一个 d p dp dp 数组,其 d p [ i ] dp[i] dp[i] 含义为将加密串前 i i i 位都解密完成使用的最后一个单词的下标(从 1 1 1 开始),如果不能用现有单词将加密串前 i i i 个字符解密,那么 d p [ i ] = = 0 dp[i]==0 dp[i]==0 。
那么首先我们可以将所有单词加入一颗字典树,注意忽略掉大小写,我们只要在终点记录下此时是下标为多少的单词即可(因为加密串不区分大小写,所以如果两个串忽略大小写一样,那么最后答案选哪个都可以,所以我们只要保留一个就可以)。
为什么我们求出 d p [ n ] dp[n] dp[n] 就可以解出答案呢。因为我们得知 d p [ n ] dp[n] dp[n] 就首先可以得知最后一个用的是哪个单词,然后我们可以令 n = n − 单 词 长 度 n = n - 单词长度 n=n−单词长度 得知新的 d p [ n ] dp[n] dp[n] ,这样以此类推我们就可以得知全部的单词。(首先题目保证了有解,所以 d p [ n ] dp[n] dp[n] 一定有解,我们求出 d p [ n ] dp[n] dp[n] 是保证了前 n n n 个都解密的,所以往前推的时候不可能无解)
我们现在就来求得 d p dp dp 数组,首先第一重循环从小到大枚举加密串下标,我们期望求出此点的 d p [ i ] dp[i] dp[i] ,那么第二重循环就是从 i i i 到 0 0 0 (因为加密的时候翻转了)在字典树上跳了,其实就是枚举其可能构成的最后一个单词,我们找到合理的 d p [ i ] dp[i] dp[i] 的条件自然是,我们跳到某个点 j j j 时, 字典树上有单词终点的标记,且 d p [ j − 1 ] dp[j -1] dp[j−1] 不为 0 0 0 ,符合这种条件就可以将 d p [ i ] dp[i] dp[i] 更新并退出第二重循环,我们只要找到某一种情况可以使得解密前 i i i 个字符就好,如果我们遍历所有情况都找不到,那么 d p [ i ] dp[i] dp[i] 只能为 0 0 0 了。最终我们就可以利用第三段中讲述的方法,用 d p [ n ] dp[n] dp[n] 一步步推出每个单词,最终倒序输出即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 2;
int n, m;
int p[N][30], tot, dp[100005];
int idx[N];
string all, s[100005];
vector<string> ans;
void insert(string x, int id) {
int root = 0;
for (int i = 0; i < x.length(); ++i) {
int cur;
if (x[i] >= 'a') cur = x[i] - 'a';
else cur = x[i] - 'A';
if (!p[root][cur]) p[root][cur] = ++tot;
root = p[root][cur];
}
idx[root] = id;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
scanf("%d", &n);
cin >> all;
scanf("%d", &m);
for (int i = 1; i <= m; ++i) {
cin >> s[i];
insert(s[i], i);
}
for (int i = 0; i < all.length(); ++i) {
int root = 0;
for (int j = i; j >= 0; --j) {
int cur = all[j] - 'a';
if (!p[root][cur]) break;
root = p[root][cur];
if (idx[root] && (j == 0 || dp[j - 1])) {
dp[i] = idx[root];
break;
}
}
}
int sta = n - 1;
while(dp[sta]) {
ans.push_back(s[dp[sta]]);
sta = sta - s[dp[sta]].length();
}
for (int i = (int)ans.size() - 1; i >= 0; --i) {
cout << ans[i] << ' ';
}
cout << endl;
}