(Remember the Word ,LA 3942)
题目来源:https://vjudge.net/problem/UVALive-3942
题意:给定一个字符串S以及n个单词,字符用这n个单词进行拆分,输出拆分的方案数。
思路:dp+字典树
可以先将这n个单词存储于字典树中,并记dp[i]:字符i开始的字符串的分解方案数量(即后缀S[i...end]),则动态转移方程为dp[i]=sum{dp[i+len(x)] | 单词x是S[i...end]的前缀}.
找S[i...end]的前缀可以在字典树中查找。
AC代码:
#define _CRT_SECURE_NO_DEPRECATE #include<iostream> #include<cmath> #include<algorithm> #include<cstring> #include<vector> #include<string> #include<iomanip> #include<map> #include<stack> #include<set> #include<queue> #include<cstdio> using namespace std; #define N_MAX 4000+5 #define L_MAX 4000*100+5 #define INF 0x3f3f3f3f #define MOD 20071027 typedef long long ll; int n; int dp[L_MAX]; int len[N_MAX]; char S[L_MAX]; struct Trie { int ch[L_MAX][26]; int val[L_MAX]; int sz; Trie() { sz = 1; memset(ch[0], 0, sizeof(ch[0])); } void init() { sz = 1; memset(ch[0], 0, sizeof(ch[0])); } int idx(char c) { return c - 'a'; } void insert(char*s, int v) { int u = 0, n = strlen(s); for (int i = 0; i < n; i++) { int c = idx(s[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; } //找长度为len的字符串s的前缀 void find(char *s, int len, vector<int>&ans) { int u = 0; for (int i = 0; i < len; i++) { if (s[i] == '\0')break; int c = idx(s[i]); if (!ch[u][c]) break; u = ch[u][c]; if (val[u] != 0)ans.push_back(val[u]); } } }; Trie trie;//不要放到main()下 int main() { int Case = 1; while (scanf("%s%d", S, &n) !=EOF) { memset(dp, 0, sizeof(dp)); trie.init(); for (int i = 1; i <= n; i++) { char x[100]; scanf("%s", x); len[i] = strlen(x); trie.insert(x, i);//将单词存入字典树,单词编号为i } int L = strlen(S); dp[L] = 1; for (int i = L - 1; i >= 0; i--) { vector<int>ans;//存储单词的编号 trie.find(S + i, L - i, ans); for (int j = 0; j < ans.size(); j++) { dp[i] = (dp[i] + dp[i + len[ans[j]]]) % MOD; } } printf("Case %d: %d\n", Case++, dp[0]); } return 0; }