d(i)表示从i开始的后缀即S[i, L-1]的分解方法数,字符串为S[0, L-1]
则有d(i) = sum{ d(i+len(x)) | 单词x是S[i, L-1]的前缀 }
递推边界为d(L) = 1,代表空串。
将前n个单词构造一颗Tire树,在树中查找后缀的过程中遇到一个单词节点就代表找到一个状态转移中的x
1 #include <cstdio> 2 #include <cstring> 3 4 const int maxnode = 400000 + 10; 5 const int maxl = 100 + 10; 6 const int sigma_size = 26; 7 const int MOD = 20071027; 8 9 char s[maxnode]; 10 int l; 11 int d[maxnode]; 12 char s1[maxl]; 13 14 int sz; 15 int ch[maxnode][sigma_size]; 16 int val[maxnode]; 17 18 inline int idx(char c) { return c - 'a'; } 19 20 void Init() { sz = 1; memset(ch[0], 0, sizeof(ch[0])); } 21 22 void insert(char* s, int v) 23 { 24 int n = strlen(s), u = 0; 25 for(int i = 0; i < n; i++) 26 { 27 int c = idx(s[i]); 28 if(!ch[u][c]) 29 { 30 memset(ch[sz], 0, sizeof(ch[sz])); 31 val[sz] = 0; 32 ch[u][c] = sz++; 33 } 34 u = ch[u][c]; 35 } 36 val[u] = v; 37 } 38 39 void solve() 40 { 41 for(int i = l-1; i >= 0; i--) 42 { 43 int u = 0; 44 for(int j = i; j < l; j++) 45 { 46 int c = idx(s[j]); 47 if(!ch[u][c]) break; 48 u = ch[u][c]; 49 if(val[u]) d[i] = (d[i] + d[j + 1]) % MOD; 50 } 51 } 52 } 53 54 int n; 55 int main() 56 { 57 //freopen("in.txt", "r", stdin); 58 59 int kase = 0; 60 while(scanf("%s", s) == 1) 61 { 62 Init(); 63 l = strlen(s); 64 scanf("%d", &n); 65 while(n--) { scanf("%s", s1); insert(s1, 1); }//val[i] = 1表示这是一个单词节点 66 memset(d, 0, sizeof(d)); 67 d[l] = 1; 68 solve(); 69 printf("Case %d: %d ", ++kase, d[0] % MOD); 70 } 71 72 return 0; 73 }