传送门:LA 3942
题意
给定一个字符串, 在给出s个单词, 求字符串由这些单词组成的方案数(单词可重复)
题解
类似于dfs的深搜方案数求解, 区别在于组成单位是单词, 所以可以用trie数保存单词, 搜索时找到单词搜索, 记忆化搜索可以降低时间消耗
建完树之后用dp逆序也是可以做的, 和dfs的原理一样
AC code
dfs 树存储是以节点方式
/*
adrui's submission
Language : C++
Result : Accepted
Love : yy
Favorite : Dragon Balls
Standing in the Hall of Fame
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<iostream>
#include<bitset>
#include<map>
using namespace std;
#define M(a, b) memset(a, b, sizeof(a))
#define mid ((l + r) >> 1)
#define ls rt << 1, l, mid
#define rs rt << 1|1, mid + 1, r
#define lowbit(x) (x & (-x))
#define LL long long
#define REP(n) for(int i = 0; i < n; i++)
#define debug 0
const int mod(20071027);
const int maxn(3e5 + 5);
int dp[maxn];
int idx(char p) {
return p - 'a';
}
struct TrieTree {
bool exist;
TrieTree *next[26];
TrieTree() {
M(next, 0);
//cnt = 0;
exist = false;
}
void insert_node(char *s) {
TrieTree *root = this;
char *p = s;
while (*p) {
int tmp = idx(*p);
if (root->next[tmp] == NULL) {
root->next[tmp] = new TrieTree();
}
root = root->next[tmp];
++p;
}
root->exist = true;
}
int dfs(char *s, int tmp, int len) {
if (tmp > len) return 0;
if (tmp == len) return 1;//找到串尾方案为1
if (dp[tmp] != -1) return dp[tmp];
dp[tmp] = 0;
TrieTree *root = this;
char *p = s;
int cnt = 1;
while (*p) {
int temp = idx(*p);
if (root->next[temp] == NULL) {//树节点为空退出搜索
break;
}
root = root->next[temp];
if (root->exist) dp[tmp] += dfs(p + cnt, tmp + cnt, len);//如果单词存在, 深搜
++cnt;
++p;
}
return dp[tmp] %= mod;
}
};
int main() {
#if debug
freopen("in.txt", "r", stdin);
#endif //debug
cin.tie(0);
cin.sync_with_stdio(false);
char s[maxn];
char word[105];
int flag = 0, cnt, kase = 0;
while (cin >> s) {
TrieTree *root = new TrieTree();//循环里
cout << "Case " << ++kase << ": ";
cin >> cnt;
while (cnt--) {
cin >> word;
root->insert_node(word);
}
M(dp, -1);
int ans = root->dfs(s, 0, strlen(s));
cout << ans << endl;
}
return 0;
}
逆序dp
/*
adrui's submission
Language : C++
Result : Accepted
Love : yy
Favorite : Dragon Balls
Standing in the Hall of Fame
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<iostream>
#include<bitset>
#include<map>
using namespace std;
#define M(a, b) memset(a, b, sizeof(a))
#define mid ((l + r) >> 1)
#define ls rt << 1, l, mid
#define rs rt << 1|1, mid + 1, r
#define lowbit(x) (x & (-x))
#define LL long long
#define REP(n) for(int i = 0; i < n; i++)
#define debug 0
const int mod(20071027);
const int maxn(5e5 + 5);
int dp[maxn];
int idx(char p) {
return p - 'a';
}
struct TrieTree {
bool exist;
TrieTree *next[26];
TrieTree() {
M(next, 0);
//cnt = 0;
exist = false;
}
void insert_node(char *s) {
TrieTree *root = this;
char *p = s;
while (*p) {
int tmp = idx(*p);
if (root->next[tmp] == NULL) {
root->next[tmp] = new TrieTree();
}
root = root->next[tmp];
++p;
}
root->exist = true;
}
int Dp(char *s, int len) {
dp[len] = 1;
for (int i = len - 1; i >= 0; --i) {
TrieTree *root = this;//根节点
for (int j = i; j < len; ++j) {
int temp = idx(s[j]);
//cout << s[j] << endl;
if (root->next[temp] == NULL) {//同dfs退出原理
break;
}
root = root->next[temp];
if (root->exist == true) {
dp[i] = (dp[i] + dp[j + 1]) % mod;//更新方案数
//cout << j + 1 << " " << len << endl;
}
}
dp[i] %= mod;
}
return dp[0];
}
};
int main() {
#if debug
freopen("in.txt", "r", stdin);
#endif //debug
cin.tie(0);
cin.sync_with_stdio(false);
char s[maxn];
char word[105];
int flag = 0, cnt, kase = 0;
while (cin >> s) {
TrieTree *root = new TrieTree();
cout << "Case " << ++kase << ": ";
cin >> cnt;
while (cnt--) {
cin >> word;
root->insert_node(word);
}
M(dp, 0);
int ans = root->Dp(s,strlen(s));
cout << ans << endl;
}
return 0;
}
总结
这题下午想思路半小时, 第一发代码10分钟, debug一小时…
把根节点开辟放在了循环外面, 好气, 还以为dfs写的搓了, 找出bud的时候表情那个精彩啊
果然结构体写得还是不熟练, 一定要上心了