/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}
P12238 [蓝桥杯 2023 国 Java A] 单词分类
题目描述
在遥远的 LQ 国,只存在三种字符:l\\tt{l}l、q\\tt{q}q 和 b\\tt{b}b(ASCII 码分别为 108108108、113113113、989898),所有的单词都由这三种字符组合而来。小蓝为了更加快速的记忆单词,决定将词典上所有的单词按照单词前缀将其分为 KKK 类,具体的要求是:
现在已知字典上一共有 NNN 个单词,小蓝想要知道将这 NNN 个单词按照上述要求分为 KKK 类,一共有多少种不同的方案。两个方案不同指的是两个方案各自选出的 KKK 个单词前缀不完全相同。答案可能过大,所以你需要将答案对 1 000 000 0071\\,000\\,000\\,0071000000007(即 109+710^9 + 7109+7)取模后输出。
输入格式
输入的第一行包含两个整数 NNN 和 KKK;
接下来 NNN 行,每行包含一个单词,由 l\\tt{l}l、q\\tt{q}q、b\\tt{b}b 三种字符组成。
输出格式
输出一个整数表示答案。答案可能很大,请输出答案对 1 000 000 0071\\,000\\,000\\,0071000000007 取模的值。
输入输出样例 #1
输入 #1
4 2
lqb
lql
qqq
qql
输出 #1
4
说明/提示
样例说明
- 方案 1:l=lqb,lql\\tt{l}=\\tt{lqb}, \\tt{lql}l=lqb,lql、q=qqq,qql\\tt{q}=\\tt{qqq}, \\tt{qql}q=qqq,qql;
- 方案 2:lq=lqb,lql\\tt{lq}=\\tt{lqb}, \\tt{lql}lq=lqb,lql、q=qqq,qql\\tt{q}=\\tt{qqq}, \\tt{qql}q=qqq,qql;
- 方案 3:l=lqb,lql\\tt{l}=\\tt{lqb}, \\tt{lql}l=lqb,lql、qq=qqq,qql\\tt{qq}=\\tt{qqq}, \\tt{qql}qq=qqq,qql;
- 方案 4:lq=lqb,lql\\tt{lq}=\\tt{lqb}, \\tt{lql}lq=lqb,lql、qq=qqq,qql\\tt{qq}=\\tt{qqq}, \\tt{qql}qq=qqq,qql。
以方案 111 为例,他表示选出的两类对应的前缀分别是 l\\tt ll 和 q\\tt qq,属于前缀 l\\tt ll 的单词有 lqb\\tt {lqb}lqb、lql\\tt{lql}lql,属于前缀 q\\tt qq 的单词有 qqq\\tt{qqq}qqq、qql\\tt{qql}qql,方案 111 将四个单词按照前缀分成了两类,且每类至少包含一个单词,每个单词仅属于一类,所以方案 111 满足题意。
评测用例规模与约定
- 对于 30%30\\%30% 的评测用例,1≤N≤101 \\leq N \\leq 101≤N≤10,1≤K≤51 \\leq K \\leq 51≤K≤5;
- 对于 50%50\\%50% 的评测用例,1≤N≤501 \\leq N \\leq 501≤N≤50,1≤K≤101 \\leq K \\leq 101≤K≤10;
- 对于所有评测用例,1≤N≤2001 \\leq N \\leq 2001≤N≤200,1≤K≤1001 \\leq K \\leq 1001≤K≤100,1≤1 \\leq1≤ 单词长度 ≤10\\leq 10≤10。
C++实现
#include"bits/stdc++.h"
using namespace std;
const int N = 205 * 10, K = 105, mod = 1e9 + 7;
int n, k;
int trie[N][4], word[N], tot;
vector<int>g[N];
inline int get_num(char c) {
if (c == 'l')return 1;
if (c == 'q')return 2;
return 3;
}
void insert(string s) {
int u = 0;
for (char c : s) {
int a = get_num(c);
if (!trie[u][a]) {
trie[u][a] = ++tot;
g[u].push_back(tot);//建图
}
u = trie[u][a];
}
word[u]++;
}
long long dp[N][K];
void dfs(int u) {
if (u && word[u]) {//叶子节点
dp[u][1] = 1;
return;
}
dp[u][0] = 1;//初始状态
for (int v : g[u]) {
dfs(v);
long long t[K] = {0};//临时数组存储结果,避免计算时新计算的值影响后面的计算
for (int i = 0; i <= k; i++) {
for (int j = 0; j <= k – i; j++) {
t[i + j] = (t[i + j] + dp[u][i] * dp[v][j]) % mod;//枚举计算
}
}
memcpy(dp[u], t, sizeof dp[u]);//赋值回来
}
dp[u][1] = (dp[u][1] + 1) % mod;//单独选择该节点
}
signed main() {
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
cin >> n >> k;
for (int i = 1; i <= n; i++) {
string s;
cin >> s;
insert(s);//建 Trie 树
}
dfs(0);//树形 DP
cout << dp[0][k];
return 0;
}

后续
接下来我会不断用C++来实现信奥比赛中的算法题、GESP考级编程题实现、白名单赛事考题实现,记录日常的编程生活、比赛心得,感兴趣的请关注,我后续将继续分享相关内容
网硕互联帮助中心



评论前必须登录!
注册