字典树Trie
字典树Trie
结构:
trie的结构就是一个类似于前缀匹配的结构,我们用 \(\delta (u,c)\) 表示节点 \(u\) 指向的下一个存储的信息为 \(c\) 的节点编号。
应用:
简单查找字符串:
建立一颗由小写字母为字符集组成的Trie树,每次查询都从根节点开始,一个一个向下查找,如果在某个节点发现没有下个节点的字母与当前要询问的名字的字母相同时,则为 WRONG,如果存在该名字,我们对这个末尾节点开一个计数的数,如果这个数 \(>0\),那么就输出 REPEAT,否则输出 OK。
code:
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 1e4 + 7;
int cnt = 1,trie[MAXN * 50][27];
int has[MAXN * 50];
int n,m;
string s;
void insert(string s){
int len = s.length(),p = 1;
for(int i = 1;i <= len;i++){
if(trie[p][s[i - 1] - 'a']) p = trie[p][s[i - 1] - 'a'];
else cnt++,trie[p][s[i - 1] - 'a'] = cnt,p = cnt;
}
has[p] = 1;
}
int find(string s){
int len = s.length(),p = 1;
for(int i = 1;i <= len;i++){
if(trie[p][s[i - 1] - 'a']) p = trie[p][s[i - 1] - 'a'];
else return 2;
}
if(has[p] == 1) return has[p]++,0;
else return has[p]++,1;
}
int main(){
scanf("%d", &n);
for(int i = 1;i <= n;i++) cin >> s,insert(s);
scanf("%d", &m);
for(int i = 1;i <= m;i++){
cin >> s;
if(find(s) == 0) printf("OK\n");
else if(find(s) == 1) printf("REPEAT\n");
else printf("WRONG\n");
}
return 0;
}
代码好实现。
最长异或路径
[P4551 最长异或路径](P4551 最长异或路径)
我们已知异或具有前缀和的性质,那么我们设根节点为 \(1\),而 \(val(x,y)\) 表示 \(x \to y\) 的路径异或值,则有:
即求 \(\max_{x,y \in [1,n]} val(x,y)\)。
我们预处理出 \(dis_x\) 数组表示 \(x\) 点到根节点( \(1\) 号节点)的路径异或和。
建立一颗 01 Trie 树,把 \(dis\) 数组的每个值从高位到低位插入Trie,深度大约为30左右。
然后枚举 \(x\) 节点,从高位到低位遍历Trie树,存在一个当前遍历到的节点的子节点满足与 \(dis_x\) 当前遍历到的二进制位上的 \(0/1\) 值相反,那么就遍历Trie树的这一个子节点。
这实际上是一种贪心的思想,因为从高位到低位,我们如果二进制位上的值相反,那么就选择这一位,因为高位上的值价值大于低位。所以我们建树的过程也是从高位往低位建树。
那么复杂度就是 \(O(nlogV)\),可过。
code:
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int MAXN = 1e5 + 7;
const int LOG = 30;
struct EDGE{ int x,val; };
int n, x, y, v;
vector<EDGE> tree[MAXN];
int val[MAXN];
int cnt = 1, trie[MAXN * LOG][2], ans;
void insert(int val){
int p = 1;
for(int i = 30;i >= 0;i--){
int op = ((val >> i) & 1);
if(trie[p][op]) p = trie[p][op];
else cnt++,trie[p][op] = cnt,p = cnt;
}
}
void dfs(int pos,int fa){
for(auto to : tree[pos]) if(to.x != fa) val[to.x] = to.val ^ val[pos],insert(val[to.x]),dfs(to.x,pos);
}
void cal(int pos){
int res = 0,p = 1;
for(int i = 30;i >= 0;i--){
int op = (val[pos] >> i) & 1;
if(trie[p][op ^ 1]) p = trie[p][op ^ 1], res = res | (1 << i);
else p = trie[p][op];
}
ans = max(ans,res);
}
void dfs1(int pos,int fa){
cal(pos);
for(auto to : tree[pos]) if(to.x != fa) dfs1(to.x,pos);
}
int main(){
scanf("%d", &n);
for(int i = 1;i <= n - 1;i++) {
scanf("%d%d%d", &x, &y, &v);
tree[x].push_back({y,v}),tree[y].push_back({x,v});
}
dfs(1,0),dfs1(1,0);
printf("%d", ans);
return 0;
}
Secret Message G
P2922 [USACO08DEC] Secret Message G
题意:
贝茜正在领导奶牛们逃跑.为了联络,奶牛们互相发送秘密信息.
信息是二进制的,共有 \(M\)(\(1 \le M \le 50000\))条,反间谍能力很强的约翰已经部分拦截了这些信息,知道了第 \(i\) 条二进制信息的前 \(b_i\)(\(1 \le b_i \le 10000\))位,他同时知道,奶牛使用 \(N\)(\(1 \le N \le 50000\))条暗号.但是,他仅仅知道第 \(j\) 条暗号的前 \(c_j\)(\(1 \le c_j \le 10000\))位。
对于每条暗号 \(j\),他想知道有多少截得的信息能够和它匹配。也就是说,有多少信息和这条暗号有着相同的前缀。当然,这个前缀长度必须等于暗号和那条信息长度的较小者。
在输入文件中,位的总数(即 \(\sum b_i + \sum c_i\))不会超过 \(500000\)。
题目中提到了 前缀 那么我们自然而然的想到Trie树去进行字符串匹配,我们可以对约翰的 \(i\) 条信息建立 \(0/1\) Trie树。对于奶牛的询问,我们可以分为两大类问题:
- 查询的字符串的前缀等于原字符串。
- 原字符串的前缀等于查询的字符串。
对于第一类问题,我们对Trie的每一个节点建立 \(pre\) 计数器,记录在这个节点上结束的信息字符串的个数。
对于第二类问题,我们也对Trie的每一个节点建立 \(val\) 计数器,记录这个节点上经过的信息字符串的数量。
统计答案时,将奶牛的信息字符串放到Trie中进行匹配,在匹配过程中,将经过的 \(pre\) 计数器加起来,在把匹配结束的节点的 \(val\) 计数器加上,就是答案了,要注意重复计数的问题!
code:
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 500005;
int n,m;
int trie[MAXN * 2][2],a[MAXN],tot = 1,val[MAXN * 2],pre[MAXN * 2];
void insert(int len){
int p = 1;
for(int i = 1;i <= len;i++){
if(trie[p][a[i]]) p = trie[p][a[i]];
else tot++,trie[p][a[i]] = tot,p = tot;
val[p]++;
}
val[p]--;
pre[p]++;
}
int find(int len){
int p = 1,res = 0;
for(int i = 1;i <= len;i++){
if(trie[p][a[i]]) p = trie[p][a[i]],res += pre[p];
else {p = trie[p][a[i]]; break;}
}
return res + val[p];
}
int main(){
scanf("%d%d", &n, &m);
for(int i = 1;i <= n;i++){
int len;scanf("%d", &len);
for(int i = 1;i <= len;i++) scanf("%d", &a[i]);
insert(len);
}
for(int i = 1;i <= m;i++){
int len;scanf("%d", &len);
for(int i = 1;i <= len;i++) scanf("%d", &a[i]);
printf("%d\n", find(len));
}
return 0;
}
本文来自博客园,作者:wyl123ly,转载请注明原文链接:https://www.cnblogs.com/wyl123ly/p/18541967

浙公网安备 33010602011771号