Trie + DP LA 3942 Remember the Word
题意:(训练指南P209) 问长字符串S能由短单词组成的方案数有多少个
分析:书上的做法。递推法,从后往前,保存后缀S[i, len-1]的方案数,那么dp[i] = sum (dp[i+len(s)])。用字典树记录并查询短单词的前缀的长度。
#include <bits/stdc++.h> using namespace std; const int L = 3e5 + 5; const int N = 4e3 + 5; const int M = 1e2 + 5; const int MOD = 20071027; char text[L], word[M]; struct Trie { int ch[N*M][26], val[N*M], sz; int idx(char c) { return c - 'a'; } void clear(void) { sz = 1; memset (ch[0], 0, sizeof (ch[0])); } void insert(char *str, int v) { int u = 0, len = strlen (str); for (int c, i=0; i<len; ++i) { c = idx (str[i]); if (!ch[u][c]) { memset (ch[sz], 0, sizeof (ch[sz])); val[sz] = 0; ch[u][c] = sz++; } u = ch[u][c]; } val[u] = v; } void query(char *str, int len, vector<int> &vec) { int u = 0; for (int c, i=0; i<len; ++i) { c = idx (str[i]); if (!ch[u][c]) return ; u = ch[u][c]; if (val[u]) vec.push_back (val[u]); } } }trie; int dp[L], l[N]; int main(void) { int cas = 0, n; while (scanf ("%s", &text) == 1) { scanf ("%d", &n); trie.clear (); for (int i=1; i<=n; ++i) { scanf ("%s", &word); l[i] = strlen (word); trie.insert (word, i); } memset (dp, 0, sizeof (dp)); int len = strlen (text); dp[len] = 1; for (int i=len-1; i>=0; --i) { vector<int> vec; trie.query (text+i, len-i, vec); for (int j=0; j<vec.size (); ++j) { dp[i] = (dp[i] + dp[i+l[vec[j]]]) % MOD; } } printf ("Case %d: %d\n", ++cas, dp[0]); } return 0; }
编译人生,运行世界!