显然,你需要一个动态规划。
用$f[i]$表示拼出串$S$前$i$个字符的方案数。
转移是显然的。枚举上一个拼接的串的长度,然后判断它是否存在,如果存在就把$f[i]$加上$f[i - l]$。
这个判断存在可以用Hash。当然可以对每个短串的反串建立Trie树,然后在Trie树上查一查$i$往前会走到长度为哪些的终止状态。
由于我懒,不想写Trie树,直接用平板电视的hash表,然后慢得起飞。
Code
1 /** 2 * UVa Live 3 * Problem#3942 4 * Accepted 5 * Time: 603ms 6 */ 7 #include <ext/pb_ds/assoc_container.hpp> 8 #include <ext/pb_ds/hash_policy.hpp> 9 #include <iostream> 10 #include <cstring> 11 #include <cstdlib> 12 #include <cstdio> 13 using namespace std; 14 using namespace __gnu_pbds; 15 typedef bool boolean; 16 17 #define ui unsigned int 18 19 const int N = 3e5 + 5, M = 20071027, L = 101; 20 const ui base = 200379; 21 22 int n; 23 char S[N], buf[L]; 24 ui ha[N], ps[N]; 25 cc_hash_table<int, boolean> mp[L]; 26 27 inline void prepare() { 28 ps[0] = 1; 29 for (int i = 1; i < N; i++) 30 ps[i] = ps[i - 1] * base; 31 } 32 33 inline boolean init() { 34 if (scanf("%s", S + 1) == EOF) return false; 35 scanf("%d", &n); 36 for (int i = 1; i < L; i++) 37 mp[i].clear(); 38 int l; 39 ui s; 40 for (int i = 1; i <= n; i++) { 41 scanf("%s", buf); 42 for (l = 0, s = 0; buf[l]; l++) 43 s = (s * base) + buf[l]; 44 mp[l][s] = true; 45 } 46 return true; 47 } 48 49 int f[N]; 50 inline void solve() { 51 f[0] = 1, ha[0] = 0; 52 n = strlen(S + 1); 53 for (int i = 1; i <= n; i++) 54 ha[i] = ha[i - 1] * base + S[i]; 55 for (int i = 1; i <= n; i++) { 56 f[i] = 0; 57 for (int j = 1; j < L && j <= i; j++) { 58 ui s = ha[i] - ha[i - j] * ps[j]; 59 if (mp[j].find(s) != mp[j].end()) 60 f[i] = (f[i] + f[i - j]) % M; 61 } 62 } 63 printf("%d ", f[n]); 64 } 65 66 int kase = 0; 67 int main() { 68 prepare(); 69 while(init()) { 70 printf("Case %d: ", ++kase); 71 solve(); 72 } 73 return 0; 74 }