Powered by:NEFU AB_IN
Trie树
模板带注释
// son[][]存储树中每个节点的子节点
// cnt[]存储以每个节点结尾的单词数量
namespace Trie{
const int N = 1e5 + 10;
int son[N][26], cnt[N], idx; // 'son[N][10]'
void init(){
memset(son, 0, sizeof son);
memset(cnt, 0, sizeof cnt);
idx = 0;
}
void insert(char *s){
int p = 0; // p代表每个节点的下标,现在为根节点
for(int i = 0; s[i]; ++ i){
int u = s[i] - 'a'; // '0'代表减数字时
// u代表p结点的子节点,但不是下标,u和p性质不一样
if(!son[p][u]) son[p][u] = ++ idx; // 新建节点,每个节点都有自己的下标
p = son[p][u]; // 往下走
}
cnt[p] ++; // 统计以p下标结尾单词的数量
}
int query(char *s){
int p = 0;
for(int i = 0; s[i]; ++ i){
int u = s[i] - 'a'; // '0'
if(!son[p][u]) return 0;
p = son[p][u];
}
return cnt[p];
}
}
using namespace Trie;
无注释
namespace Trie{
const int N = 1e5 + 10;
int son[N][26], cnt[N], idx;
void init(){
memset(son, 0, sizeof son);
memset(cnt, 0, sizeof cnt);
idx = 0;
}
void insert(char *s){
int p = 0;
for(int i = 0; s[i]; ++ i){
int u = s[i] - 'a';
if(!son[p][u]) son[p][u] = ++ idx;
p = son[p][u];
}
cnt[p] ++;
}
int query(char *s){
int p = 0;
for(int i = 0; s[i]; ++ i){
int u = s[i] - 'a';
if(!son[p][u]) return 0;
p = son[p][u];
}
return cnt[p];
}
}
using namespace Trie;
Phone List
-
题意
给定 n n n个长度不超过 10 10 10的数字串,问其中是否存在两个数字串 S , T S,T S,T,使得 S S S是 T T T的前缀,多组数据。 -
思路
T r i e Trie Trie树模板题
常规思路就是把每个字符串都进树,然后离线处理,这样时间和空间复杂度都比较大
所以我们可以做到动态查询
- 比如 91 91 91和 9134 9134 9134, 91 91 91以 1 1 1结尾,那么当 9134 9134 9134遍历到 1 1 1时,发现这个下标的 c n t ≠ 0 cnt \ne0 cnt=0,说明前面有单词 91 91 91,那么就不符合
- 比如 9134 9134 9134和 91 91 91, 91 91 91遍历到 1 1 1的时候,发现这个下标的 c n t = 0 cnt = 0 cnt=0,但是 91 91 91是 9134 9134 9134的前缀,也不符合要求。要做到这点,只需要一开始记个 f l a g = f a l s e flag = false flag=false,如果插入的过程中开点了,那么 f l a g = t u r e flag = ture flag=ture,像刚才就没有动态开点,所以依然是 f a l s e false false
/* * @Author: NEFU AB_IN * @Date: 2021-08-26 14:00:53 * @FilePath: \Vscode\ACM\Project\Trie\Poj3630.cpp * @LastEditTime: 2021-08-26 15:25:14 */ #include<iostream> #include<vector> #include<cstring> #include<string.h> #include<cstdio> #include<climits> #include<cmath> #include<algorithm> #include<queue> #include<deque> #include<map> #include<set> #include<string> #include<stack> using namespace std; #define LL long long #define MP make_pair #define SZ(X) ((int)(X).size()) #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define DEBUG(X) cout << #X << ": " << X << endl; typedef pair<int , int> PII; namespace Trie{ const int N = 1e5 + 10; int son[N][10], cnt[N], idx; void init(){ memset(son, 0, sizeof son); memset(cnt, 0, sizeof cnt); idx = 0; } bool insert(char *s){ int p = 0, flag = 0; for(int i = 0; s[i]; ++ i){ int u = s[i] - '0'; if(!son[p][u]) son[p][u] = ++ idx, flag = 1; p = son[p][u]; if(cnt[p]) return false; } cnt[p] ++; return flag; } int query(char *s){ int p = 0; for(int i = 0; s[i]; ++ i){ int u = s[i] - '0'; if(!son[p][u]) return 0; p = son[p][u]; } return cnt[p]; } } using namespace Trie; int t; char s[N]; signed main() { IOS cin >> t; while(t --){ init(); int n, flag = 0; cin >> n; for(int i = 1; i <= n; ++ i){ cin >> s; if(!insert(s)) flag = 1; } if(flag) cout << "NO\n"; else cout << "YES\n"; } return 0; }
01Trie树
模板带注释
01 01 01字典树,每个分支只有 01 01 01两个,主要解决异或问题
如果多组数据运用 01 t r i e 01trie 01trie树时,需要进行初始化,可以像正常 t r i e trie trie树一样进行 m e m s e t memset memset操作,但有些题目就会 T T T,这时我们可以采用更好的方法进行初始化。观察到分支只有两个,那么就可以在创建新分支的同时,对新分支的两个 01 01 01子节点进行初始化,注意别忘了一开始也要对根节点的子节点进行初始化!
namespace Trie01{
const int N = 1e5 + 10;
int son[N * 32][2], val[N * 32], idx; // n个数,每个数最多被拆成不超过32位
void init(){
idx = 0;
son[0][0] = son[0][1] = 0;
// val没必要进行初始化,因为是可以覆盖的
}
void insert(int x){
int p = 0;
for(int i = 31; i >= 0; -- i){
// 贪心时构建01trie树的基础,从二进制的高位开始贪心
int u = (x >> i) & 1;
if(!son[p][u]){
son[p][u] = ++ idx;
p = son[p][u];
// 构造新节点的同时初始化新节点
son[p][0] = son[p][1] = 0;
}
else p = son[p][u];
}
val[p] = x; // 记录终点
}
int query(int x){
int p = 0;
for(int i = 31; i >= 0; -- i){
int u = (x >> i) & 1;
// 贪心的选择优先走和当前位不同的路
if(son[p][u ^ 1]) p = son[p][u ^ 1];
else p = son[p][u];
}
return val[p];
}
}
using namespace Trie01;
无注释
namespace Trie01{
const int N = 1e5 + 10;
int son[N * 32][2], val[N * 32], idx;
void init(){
idx = 0;
son[0][0] = son[0][1] = 0;
}
void insert(int x){
int p = 0;
for(int i = 31; i >= 0; -- i){
int u = (x >> i) & 1;
if(!son[p][u]){
son[p][u] = ++ idx;
p = son[p][u];
son[p][0] = son[p][1] = 0;
}
else p = son[p][u];
}
val[p] = x;
}
int query(int x){
int p = 0;
for(int i = 31; i >= 0; -- i){
int u = (x >> i) & 1;
if(son[p][u ^ 1]) p = son[p][u ^ 1];
else p = son[p][u];
}
return val[p];
}
}
using namespace Trie01;
Xor Sum(hdu4825)
-
题意
n n n个正整数, m m m次询问,找一个正整数 K K K,使得 K K K 与 S S S 的异或结果最大 -
思路
01 T r i e 01Trie 01Trie板子题/* * @Author: NEFU AB_IN * @Date: 2021-08-26 16:40:07 * @FilePath: \Vscode\ACM\Project\Trie\hdu4825.cpp * @LastEditTime: 2021-08-26 22:54:39 */ #include<bits/stdc++.h> using namespace std; #define LL long long #define MP make_pair #define SZ(X) ((int)(X).size()) #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define DEBUG(X) cout << #X << ": " << X << endl; typedef pair<int , int> PII; namespace Trie01{ const int N = 1e5 + 10; int son[N * 32][2], val[N * 32], idx; void init(){ idx = 0; son[0][0] = son[0][1] = 0; } void insert(int x){ int p = 0; for(int i = 31; i >= 0; -- i){ int u = (x >> i) & 1; if(!son[p][u]){ son[p][u] = ++ idx; p = son[p][u]; son[p][0] = son[p][1] = 0; } else p = son[p][u]; } val[p] = x; } int query(int x){ int p = 0; for(int i = 31; i >= 0; -- i){ int u = (x >> i) & 1; if(son[p][u ^ 1]) p = son[p][u ^ 1]; else p = son[p][u]; } return val[p]; } } using namespace Trie01; int t, n, m, x; signed main() { IOS while(~scanf("%d", &t)){ for(int j = 1; j <= t; ++ j){ init(); scanf("%d%d", &n, &m); for(int i = 1; i <= n; ++ i){ scanf("%d", &x); insert(x); } printf("Case #%d:\n", j); for(int i = 1; i <= m; ++ i){ scanf("%d", &x); printf("%d\n", query(x)); } } } return 0; }
A - L 语言
-
题意
给出 n n n个字符串构成一个字典 D D D, m m m次询问,每次询问一个字符串,输出这段字符串在字典 D D D可以被理解的最长前缀的位置 -
思路
dp + trie
思路都在注释中
这里的查询函数改了,改成了从字符串的某处开始查询
主要思路
- 先,从头开始进行 d p dp dp,遇到了前缀就标记 d p [ ] = 1 dp[] = 1 dp[]=1记作下次 d p dp dp的起点
- 后,从后往前遍历,最先 d p [ ] = 1 dp[] = 1 dp[]=1的下标就是最佳答案
/* * @Author: NEFU AB_IN * @Date: 2021-08-27 01:01:30 * @FilePath: \Vscode\ACM\Project\Trie\L.cpp * @LastEditTime: 2021-08-27 01:34:09 */ #include<bits/stdc++.h> using namespace std; #define LL long long #define MP make_pair #define SZ(X) ((int)(X).size()) #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define DEBUG(X) cout << #X << ": " << X << endl; typedef pair<int , int> PII; namespace Trie{ const int N = 2e6 + 20; int n, m, dp[N]; int son[N][26], cnt[N], idx; char s[N]; void init(){ memset(son, 0, sizeof son); memset(cnt, 0, sizeof cnt); idx = 0; } void insert(char *s){ int p = 0; for(int i = 0; s[i]; ++ i){ int u = s[i] - 'a'; if(!son[p][u]) son[p][u] = ++ idx; p = son[p][u]; } cnt[p] ++; } void query(int x){ // the searching start index of string s int p = 0; for(int i = x; s[i]; ++ i){ int u = s[i] - 'a'; if(!son[p][u]) break; p = son[p][u]; if(cnt[p]) dp[i] = 1; // explain there exists a word's end, so next time // dp will call this function, the param is id plus one } } } using namespace Trie; signed main() { IOS; cin >> n >> m; for(int i = 1; i <= n; ++ i){ cin >> s; insert(s); } for(int j = 1; j <= m; ++ j){ memset(dp, 0, sizeof dp); cin >> s + 1; dp[0] = 1; // init for(int i = 0; s[i]; ++ i){ if(dp[i]) query(i + 1); } for(int i = strlen(s); i >= 0; -- i){ // searching from end to start, in order to gain the max number depend // on greedy thought, and has made sure if there r not exist a solution // then output 0 because of dp[0] = 1 if(dp[i]){ cout << i << '\n'; break; } } } return 0; }
B - Secret Message 秘密信息
-
题意
有多少信息和这条暗号有着相同的前缀,这些信息和暗号都是不完整的 -
思路
既然是不完整的,那么暗号里只要找到在 t r i e trie trie可以匹配的即可
比如 11 11 11 配对 1 , 110 1,110 1,110,就分成了两种情况
- 一种就是像 1 1 1这种在匹配路上
- 一种就是像 110 110 110正常结束的
/* * @Author: NEFU AB_IN * @Date: 2021-08-27 02:41:04 * @FilePath: \Vscode\ACM\Project\Trie\SecretMessage.cpp * @LastEditTime: 2021-08-27 09:57:06 */ #include<bits/stdc++.h> using namespace std; #define LL long long #define MP make_pair #define SZ(X) ((int)(X).size()) #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define DEBUG(X) cout << #X << ": " << X << endl; typedef pair<int , int> PII; namespace Trie{ const int N = 2e6 + 10; int son[N][2], cnt[N], idx, ans[N]; char s[N]; void init(){ memset(son, 0, sizeof son); memset(cnt, 0, sizeof cnt); idx = 0; } void insert(char *s){ int p = 0; for(int i = 0; s[i]; ++ i){ int u = s[i] - '0'; if(!son[p][u]) son[p][u] = ++ idx; p = son[p][u]; ans[p] ++; } cnt[p] ++; } int query(char *s){ int p = 0, tot = 0, flag = 0; for(int i = 0; s[i]; ++ i){ int u = s[i] - '0'; if(!son[p][u]) { flag = 1; break; } p = son[p][u]; tot += cnt[p]; } if(!flag) tot += ans[p] - cnt[p]; return tot; } } using namespace Trie; int n, m; signed main() { IOS; cin >> n >> m; for(int i = 1; i <= n; ++ i){ int k, idx = 0; cin >> k; for(int j = 1; j <= k; ++ j){ char x; cin >> x; s[idx ++] = x; } s[idx] = '\0'; insert(s); } for(int i = 1; i <= m; ++ i){ int k, idx = 0; cin >> k; for(int j = 1; j <= k; ++ j){ char x; cin >> x; s[idx ++] = x; } s[idx] = '\0'; cout << query(s) << '\n'; } return 0; }
D - The XOR Largest Pair
-
题意
在给定的 N N N 个整数 A 1 , A 2 , … , A N A1,A2,…,AN A1,A2,…,AN 中选出两个进行异或运算,得到的结果最大是多少? -
思路
O ( n ) O(n) O(n)遍历的 01 T r i e 01Trie 01Trie/* * @Author: NEFU AB_IN * @Date: 2021-08-27 10:11:04 * @FilePath: \Vscode\ACM\Project\Trie\poj3764.cpp * @LastEditTime: 2021-08-27 10:32:58 */ #include<iostream> #include<vector> #include<cstring> #include<string.h> #include<cstdio> #include<climits> #include<cmath> #include<algorithm> #include<queue> #include<deque> #include<map> #include<set> #include<string> #include<stack> using namespace std; #define LL long long #define MP make_pair #define SZ(X) ((int)(X).size()) #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define DEBUG(X) cout << #X << ": " << X << endl; typedef pair<int , int> PII; namespace Trie01{ const int N = 1e5 + 10; int son[N * 32][2], val[N * 32], idx; void init(){ idx = 0; son[0][0] = son[0][1] = 0; } void insert(int x){ int p = 0; for(int i = 31; i >= 0; -- i){ int u = (x >> i) & 1; if(!son[p][u]){ son[p][u] = ++ idx; p = son[p][u]; son[p][0] = son[p][1] = 0; } else p = son[p][u]; } val[p] = x; } int query(int x){ int p = 0; for(int i = 31; i >= 0; -- i){ int u = (x >> i) & 1; if(son[p][u ^ 1]) p = son[p][u ^ 1]; else p = son[p][u]; } return val[p] ^ x; } } using namespace Trie01; int n, a[N]; signed main() { cin >> n; for(int i = 1; i <= n; ++ i){ cin >> a[i]; insert(a[i]); } int ans = a[1] ^ a[2]; for(int i = 1; i <= n; ++ i){ ans = max(ans, query(a[i])); } cout << ans << '\n'; return 0; }