UVALive 3942 Remember the Word(字典树+DP)
题意:一个长字符串和多个短字符串,求短字符串有多少种方式组成长字符串。
状态转移方程: dp[i] = sum(d[i + len(x)]) (x是s[i...L]的前缀)
对于每个i,如果直接暴力寻找s[i...L]的前缀,复杂度为O(nm) (n为短字符串的个数,m为短字符串的长度),肯定会超时。
既然是求前缀,那么可以使用前缀树(字典树)来解决此问题。用字典树寻找前缀的复杂度为O(m)。
#include <cstdio> #include <iostream> #include <cstring> #define maxn 400100 #define maxm 300010 #define mod 20071027 #define sigma_size 26 using namespace std; char str[maxm], tstr[110]; int dp[maxm], ch[maxn][sigma_size], val[maxn], sz; struct Trie { int get_id(char c) { return c - 'a'; } void insert(char* str, int v) { int u = 0, len = (int)strlen(str); for (int i = 0; i < len; ++i) { int id = get_id(str[i]); if (!ch[u][id]) { memset( ch[sz], 0, sizeof(ch[sz])); val[sz] = 0; ch[u][id] = sz++; } u = ch[u][id]; } val[u] = v; } int query(char* str, int start) { int u = 0, result = 0, len = (int)strlen(str), next; for (int i = 0; i < len; ++i) { int id = get_id(str[i]); next = ch[u][id]; if (next) { if (val[next]) result = (result + dp[i + start + 1])%mod; } else break; u = next; } return result; } }; void init(); int main(void) { int ca = 1, n; while (scanf("%s", str) != EOF) { init(); int len = (int)strlen(str); scanf("%d", &n); Trie trie = Trie(); while (n--) { scanf("%s", tstr); trie.insert( tstr, 1); } dp[len] = 1; for (int i = len - 1; i >= 0; i--) { dp[i] = trie.query( str + i, i); } printf("Case %d: %d\n", ca++, dp[0]); } return 0; } void init() { sz = 1; memset( dp, 0, sizeof(dp)); memset( ch[0], 0, sizeof(ch[0])); memset( val, 0, sizeof(val)); }
posted on 2015-10-30 16:03 chuninsane 阅读(114) 评论(0) 编辑 收藏 举报