标签:ber 知识点 模式 rem else span 说明 main 题解
知识点: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\n", cnt, dp[n]);
}
return 0;
}
标签:ber 知识点 模式 rem else span 说明 main 题解
原文地址:https://www.cnblogs.com/zcr-blog/p/14901189.html