作业介绍
// s 当前要插入的字符串
#include <bits/stdc++.h>
using namespace std;
const int N = 3e6 + 10;
int T, n, m;
// trie[节点个数][每个节点最多能拓展出几条边]
// 大写字母(26),小写字母(26), 数字(10)
int trie[N][65], cnt;
int sum[N];
int get(char c) {
if (c >= 'A' && c <= 'Z')
return c - 'A';
if (c >= 'a' && c <= 'z')
return c - 'a' + 26;
return c - '0' + 52;
}
// l 字符串 s 的长度
void insert(string s) { // 插入字符串
int l = s.size();
int p = 0;
for (int i = 0; i < l; i++) {
int c = get(s[i]);
if (!trie[p][c])
trie[p][c] = ++cnt; // 如果没有,就添加结点
p = trie[p][c];
sum[p]++; // 经过这点的字符串的数量增加
}
}
int find(string s) { // 查找字符串
int p = 0, l = s.size();
int res = 0;
for (int i = 0; i < l; i++) {
int c = get(s[i]);
if (!trie[p][c])
return 0;
p = trie[p][c];
// 查找 s 的时候经过 p 点,那么p 点 可能 是 s 的前缀
}
return sum[p];
}
int main() {
cin >> T;
while (T--) {
cin >> n >> m;
for (int i = 0; i <= cnt; i++) {
sum[i] = 0;
for (int j = 0; j <= 62; j++)
trie[i][j] = 0;
}
cnt = 0;
for (int i = 1; i <= n; i++) {
string s;
cin >> s;
insert(s);
}
for (int i = 1; i <= m; i++) {
string s;
cin >> s;
cout << find(s) << endl;
}
}
return 0;
}
// n 条二进制信息,每条长度 b[i],
// m 条查询密码,每条长度 c[j]
// 对于每条密码,统计有多少条信息满足:两者前 min(b[i],c[j]) 位完全相同
// 二进制 trie 前缀树
// pass[p] , 有多少条信息经过节点 p ,
// ed[p] 有多少条信息在节点 p 结束
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+10;
int trie[N][2], pass[N], ed[N], idx;
int a[N], n, m;
void insert(int len, int a[]) {
int p = 0;
for (int i = 0; i < len; i++) {
int c = a[i];
if (!trie[p][c])
trie[p][c] = ++idx;
p = trie[p][c];
pass[p]++; // 经过该节点的字符串个数
}
ed[p]++; // 以该字符结尾的个数
}
int query(int len, int a[]) {
int p = 0, res = 0;
for (int i = 0; i < len; i++) {
int c = a[i];
if (!trie[p][c])
return res;
p = trie[p][c];
res += ed[p]; // 加上与当前节点为结束的个数
}
// 加上前缀相同且长度比它长的字符串个数
return res - ed[p] + pass[p];
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
int len;
cin >> len;
for (int j = 0; j < len; j++)
cin >> a[j];
insert(len, a);
}
while (m--) {
int len;
cin >> len;
for (int j = 0; j < len; j++)
cin >> a[j];
cout << query(len, a) << endl;
}
return 0;
}
异或: 相同为 0 不同为 1
//建字典树:第一个数直接建字典树
//
//从第二个数字开始,
//建立字典树的时候,如果有相反的边,走这条边
// 对应的这一个位, 值就是 1
//如果没有相反的边, 这一位 就是 0
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int trie[N * 32][2], idx;
void insert() {
// ...
}
int query(int x) {
int p = 0, res = 0;
for (int i = 30 ; i >= 0; i--) {
int u = x >> i & 1; // 得到第 x 位的值
if (trie[p][!u]) // 优先走相反的位
res |= 1 << i, p = trie[p][!u];
else
p = trie[p][u];
}
return res;
}
int main() {
cin >> n;
int ans = 0;
for (int i = 1; i <= n; i++) {
cin >> x;
if (x > 1)
ans = max(ans, query(x));
insert(x);
}
cout << ans;
return 0;
}
// 字典,
// 字符串,问你在字典里面,
// 字符串最多能翻译到多少
// trie + DP
// f[i] : 能翻译到 i
// 状态转移: f[i] = |f[j] [j+1, i]之间有单词
// [j+1, i]之间有单词 字典树 ,ed[i]
#include <bits/stdc++.h>
using namespace std;
const int N = 2e6 + 10;
int trie[N][26], idx;
int n, m, f[N];
bool ed[N];
void insert(string s) {
int p = 0, len = s.size();
for (int i = 0; i < len; i++) {
int u = s[i] - 'a';
if (!trie[p][u])
trie[p][u] = ++idx;
p = trie[p][u];
}
ed[p] = 1;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
string s;
cin >> s;
insert(s);
}
while (m--) {
string s;
cin >> s;
int len = s.size();
memset(f, 0, sizeof f);
f[0] = 1;
int ans = 0;
// f[i] = |f[j] [j+1, i]之间有单词
for (int i = 0; i <= len; i++) {
if (!f[i])
continue;
ans = i ;
int p = 0 ;
for (int k = i; k < min(len, i + 20) ; k++) {
int u = s[k] - 'a';
if (!trie[p][u])
break;
p = trie[p][u];
if (ed[p])
f[k + 1] = 1;
}
}
cout << ans << endl;
}
return 0;
}
// 字典树:带着信息
// cnt[p] 到达某个点的数量/是否到达
// ed[p] 以某个点结束的数量/是否到达
// 两个字符串的相同前缀, a 和 b 的前缀
// a 的长度小于 b , b 的长度小于 a
// query(b)
// a 的长度小于 b : sum(ed(p)) , a的路径上
// 1110 1110
// 11100000 11010
// b 的长度小于 a
// 1101110 11001010 1100000
// 110 经过 0 的数量
// sum[p] 统计经过 p 的数量 , ed[p] 以 p结尾的数量
int trie[N][2], sum[N], ed[N],idx;
int n,m;
void insert(int len, int a[]){
int p =0;
for(int i=0;i<len;i++){
int u = a[i];
if(!trie[p][u])
trie[p][u] = ++idx;
p = trie[p][u];
sum[p]++; // 经过这个点的数量
}
ed[p]++; // 以这个点结束的数量
}
int query(int len, int a[]){
int p =0, res =0;
for(int i=0;i<len;i++){
int u = a[i];
if(!trie[p][u])
return res;
p = trie[p][u];
res += ed[p];
}
// 累加的是比 a 长的编码的
res = res + sum[p] - ed[p];
}
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int T, n;
int trie[N][10], idx;
bool ed[N]; // 标记是否以这个结尾
bool flg;
void insert(string s) {
int p = 0, l = s.size();
for (int i = 0; i < l; i++) {
int u = s[i] - '0';
if (!trie[p][u])
trie[p][u] = ++idx;
else if (i == l - 1 && !flg) // 后输入的字符串是之前输入的字符串的前缀
flg = 1;
p = trie[p][u];
if (ed[p]) // 之前输入的字符串是现在的字符串的前缀
flg = 1;
}
ed[p] = 1;
}
int main() {
cin >> T;
while (T--) {
cin >> n;
flg = idx = 0;
memset(trie, 0, sizeof trie);
memset(ed, 0, sizeof ed);
for (int i = 1; i <= n; i++) {
string s;
cin >> s;
if (flg)
continue;
insert(s);
}
if (flg)
cout << "NO\n";
else
cout << "YES\n";
}
return 0;
}
题目
认领作业后才可以查看作业内容。
- 状态
- 正在进行…
- 题目
- 7
- 开始时间
- 2026-10-1 0:00
- 截止时间
- 2026-10-31 23:59
- 可延期
- 24 小时