知识点:dp+trie
这道题显然是从前往后dp的,题解区里还没有这样的题解,我就来发一波(题解区里也有提到不过并没有做详细的说明也没有代码)。
思路和从后往前的基本一样。
设 (dp_i)代表前缀 (s_{1...i}) 有多少种不同的组成方式。
很容易想到转移方程:
如果一个模式串 (t) 是前缀 (s_{1...i}) 的后缀,则 (dp_i += dp_{i-|t|})。
如果暴力去判断的话复杂度是 (O(nm)) 的,这道题过不去,所以我们要考虑优化。
我们发现每个模式串最长也才 (100),考虑将所有模式串倒序建成一个 (trie)。
对于 (s) 的一个前缀,将其倒叙放到 (trie) 上匹配,如果经过结束节点就说明匹配到了一个模式串,(dp) 值加上其即可。
因为模式串最长 (100),即 (trie) 树的深度最大才 (100),所以复杂度是(O(100n))的。
多组数据注意初始化。
#include <bits/stdc++.h>
using namespace std;
int trie[400010][26], tot;
char s[300010], t[110];
int n, m;
int End[400010], dp[300010];
int cnt;
int main() {
while (scanf("%s", s + 1) != EOF) {
tot = 1;
memset(End, 0, sizeof(End));
memset(trie, 0, sizeof(trie));
cnt++;
n = strlen(s + 1);
scanf("%d", &m);
for (int i = 1, len, p; i <= m; i++) {
scanf("%s", t + 1);
len = strlen(t + 1);
p = 1;
for (int j = len; j; j--) {
if (!trie[p][t[j] - 'a']) trie[p][t[j] - 'a'] = ++tot;
p = trie[p][t[j] - 'a'];
}
End[p]++;
}
memset(dp, 0, sizeof(dp));
dp[0] = 1;
for (int i = 1, p; i <= n; i++) {
p = 1;
for (int j = i; j; j--) {
if (trie[p][s[j] - 'a']) p = trie[p][s[j] - 'a'];
else break;
if (End[p]) {
dp[i] += (dp[j - 1] * End[p]) % 20071027;
dp[i] %= 20071027;
}
}
}
printf("Case %d: %d
", cnt, dp[n]);
}
return 0;
}