给出一个长度不超过300000的字符串 S,然后给出 n 个长度不超过100的字符串。
如果字符串可以多次使用,用这 n 个字符串组成 S 的方法数是多少?
比如样例中,abcd = a + b + cd = ab + cd
dp[i] 表示用这n个字符串构成,S中从 i ~ len之间的字母构成的子串,的可分解方案数。
如果存在一个位置 x >= i, 且 i~x 之间的字母是一个单词,那么dp[i] = ∑ ( dp[x] )
但是如果暴力枚举 i ~ x是不是一个单词,必然会TLE。这时我们就需要 Trie 树优化这个DP。
#include <iostream> #include <cstdlib> #include <cstdio> #include <cmath> #include <cstring> using namespace std; #define maxn 400000 + 100 #define sigma_size 27 #define LL long long #define MOD 20071027 int tot = 0; int trie[maxn][sigma_size], sum[maxn], dp[maxn]; void insert(char s[]) { int root = 0; for (int i = 0; s[i]; i++) { int id = s[i]-'a'; if (!trie[root][id]) { sum[++tot] = 0; trie[root][id] = tot; } root = trie[root][id]; } sum[root] = 1; } void found(char s[], int k) { int root = 0; for (int i = k; s[i]; i++) { int id = s[i]-'a'; if (!trie[root][id]) return; root = trie[root][id]; if (sum[root]) dp[k] = (dp[k]+dp[i+1])%MOD; } } int main() { char s[maxn]; int len, ca = 0; while(scanf("%s", s) != EOF) { char t[maxn]; int n; scanf("%d", &n); memset(trie, 0, sizeof(trie)); memset(sum, 0, sizeof(sum)); for (int i = 1; i <= n; i++) { scanf("%s", t); insert(t); } memset(dp, 0, sizeof(dp)); len = strlen(s); dp[len] = 1; for (int i = len-1; i >= 0; i--) found(s, i); printf("Case %d: %d ", ++ca, dp[0]); } }