应用:

1.前缀问题

2.异或问题(转化为前缀问题)

3.查询问题

思想:

将要进行匹配的字符串化为一颗树

字符为边,在结束位置统计该串的全部信息

操作:插入,查询,删除.etc

ac:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
using namespace std;
const int maxn=1e4+;
const int Z=;
int n,t,temp;
int trie[maxn*Z*Z][Z];//该写法调用快,但是占用空间大,视情况而定!!!trie数组节点数!!!
char num[Z];
bool isword[maxn*Z*Z],judge;
inline void insert()
{
int len=strlen(num+),pos=;
for(int i=;i<=len;i++){ int v=trie[pos][ num[i]-'' ];
//printf("i%d v%d\n",i,v); if(!v){//没有这个店->新建
trie[pos][num[i]-'']=++temp;
pos=temp;//!!注意不要提前给pos赋值!!
// if(i==10)printf("i=10:v%d\n",v);
}
else pos=v;//有这个点->继续 if(isword[pos])judge=;//该串包含某串 if(i==len){//到达字符串结尾,进行信息统计
isword[pos]=; if(v)judge=;//表示该串是某串的前缀
}
}
}
int main()
{
scanf("%d",&t);
while(t--){
memset(trie,,sizeof(trie));
memset(isword,,sizeof(isword));
judge=;temp=;//更新数据的问题 scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%s",num+);
insert();
}
if(!judge)printf("YES\n");
else printf("NO\n");
}
return ;
}
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<vector>
using namespace std;
/*template<typename T>
inline void read(T &a){
a=0;T b=1;char x=getchar();
while(x<'0'||'9'<x){
if(x=='-')b=-1;
x=getchar();
}
while('0'<=x&&x<='9'){
a=(a<<1)+(a<<3)+x-'0';
x=getchar();
}
a*=b;
}
char C[50];
int temp;
template<typename T>
inline void write(T a){
if(a<0){
putchar('-');
a=-a;
}
do{
C[++temp]=a%10+'0';
a/=10;
}while(a);
while(temp)putchar(C[temp--]);
}*/ const int maxn=1e6+;
int to[maxn][],n,m,cnt,p;
vector<int >s[maxn];
char ch[];
bool vis[maxn];
inline void insert(int p){
int u=;int len=strlen(ch+);
for(int i=;i<=len;i++){
int v=ch[i]-'a';
if(!to[u][v])to[u][v]=++cnt;
u=to[u][v];
s[u].push_back(p);
}
}
inline int query(int p){ int u=;int len=strlen(ch+);
for(int i=;i<=len;i++){
int v=ch[i]-'a';
if(!to[u][v])return ;
u=to[u][v];
}
if(!vis[u]){sort(s[u].begin(),s[u].end());vis[u]=;}
return s[u].end()-lower_bound(s[u].begin(),s[u].end(),p);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++){
scanf("%s%d",ch+,&p);
insert(p);
} for(int i=;i<=m;i++){
scanf("%s%d",ch+,&p);
printf("%d\n",query(p));
}
return ;
}

max xor:

思路,直接暴力很简单,但是非常慢,

发现一个性质,就是a xor b,每一位不一样的越多结果越大

所以想到trie树,我们把原来的数从32~1位建trie树,注意高位在前(不用担心会出现模式

串不够长的情况),保证结果最大,

查询时找 相反的边,实在没有就走相同的边,结果一定不会更差,

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=32e5+;
int to[maxn][],vis[maxn],cnt;
int n,m,num;
inline void insert(int p){
int u=;
for(int i=;i>=;i--){//取出p的32~1位
bool v=p&(<<i);
if(!to[u][v])to[u][v]=++cnt;
u=to[u][v];
}
}
inline int query(int p){
int u=,ans=;
for(int i=;i>=;i--){//取出p的32~1位
bool v=p&(<<i);
if(to[u][!v]){
u=to[u][!v];
ans=(ans<<|);
}
else {
u=to[u][v];
ans<<=;
}
}
return ans;
}
int main(){
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%d",&num);
insert(num);
}
scanf("%d",&m);
for(int i=;i<=m;i++){
scanf("%d",&num);
printf("%d\n",query(num));
}
return ;
}

最长 xor路径:

利用了xor的与自己是互逆运算的性质,

我们维护一个数组,track[i]表示节点i到根节点的 xor值

最后每个节点询问它和已知节点(到根的xor路径值)的最大xor值

并且插入该值即可:
ac:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn=32e5+; int first[maxn],next[maxn*],to[maxn*],w[maxn*],edge_count;
inline void add(int x,int y,int z){
edge_count++;
to[edge_count]=y;
w[edge_count]=z;
next[edge_count]=first[x];
first[x]=edge_count;
}
int track[maxn];
void dfs(int u,int fa){
for(int i=first[u];i;i=next[i]){
int v=to[i];
if(v==fa)continue; track[v]=track[u]^w[i];
dfs(v,u);
}
}
int e[maxn][],cnt;
inline void insert(int x){
int u=;
for(int i=;i>=;i--){//取出32~1位
bool v=x&(<<i);
if(!e[u][v])e[u][v]=++cnt;
u=e[u][v];
}
}
inline int query(int x){
int u=;
int ans=;
for(int i=;i>=;i--){
bool v=x&(<<i);
if(e[u][v^]){
u=e[u][v^];
ans=(ans<<|);
}
else{
u=e[u][v];
ans<<=;
}
}
return ans;
}
int n,ans;
int main(){
scanf("%d",&n);
for(int i=,u,v,q;i<n;i++){
scanf("%d%d%d",&u,&v,&q);
add(u,v,q);add(v,u,q);
}
dfs(,);
for(int i=;i<=n;i++){
ans=max(ans,query(track[i]));
insert(track[i]);
}
printf("%d",ans);
return ;
}

离线处理两个串的最长前缀O(nlogn)(注意数组越界问题)

/*问题描述
给出一些串,多组询问求两个串的最长公共前缀。字符串总长 < 10^6。
输入格式
第一行一个整数n,表示字符串的个数。
接下来n行,每行一个字符串(字符串不含空格)。
第n+2行一个整数m,表示询问总数。
接下来m行,每行两个整数a,b,表示询问第a个字符串和第b个字符串的最长公共前缀的长度。
输出格式
输出共m行,对于每个询问输出最长的公共前缀的长度
输入样例
5
abcdef
abcd
acd
cade
abcdef
4
1 2
2 3
1 5
3 4
输出样例
4
1
6
0
限制与约定
1<= m < 10^6
时间限制:1s
空间限制:256MB*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn=1e6+;
char ch[maxn];
int n,m,to[maxn][],cnt,vis[maxn],maxdeep;
//vis[i]表示字符串i在trie树中位置
inline void insert(int p){
int u=;int len=strlen(ch+);
maxdeep+=len;
for(int i=;i<=len;i++){
int v=ch[i]-'a';
if(!to[u][v])to[u][v]=++cnt;
u=to[u][v];
}
vis[p]=u;
//printf("%d%d\n",p,u);
}
int deep[maxn],f[maxn][],log[maxn];
void build(int u){
for(int i=;i<=log[ deep[u] ];i++)
f[u][i]=f[f[u][i-]][i-];
for(int i=;i<=;i++){
if(to[u][i]){
deep[ to[u][i] ]=deep[u]+;
f[ to[u][i] ][]=u; build(to[u][i]);
}
}
}
inline void LCA_init(){
for(int i=;i<=maxdeep;i++)log[i]=log[i>>]+;
deep[]=;//??非常关键的地方,以谁为根谁的深度就是1
build();
}
inline int LCA(int x,int y){
if(deep[x]<deep[y])swap(x,y);
for(int i=log[deep[x]];i>=;i--){
if(deep[y]<=deep[f[x][i]])x=f[x][i];
}
if(x==y)return x;
for(int i=log[deep[x]];i>=;i--){
if(f[x][i]!=f[y][i]){
x=f[x][i];y=f[y][i];
}
}
return f[x][];
}
int main(){
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%s",ch+);
insert(i);
}
scanf("%d",&m);
LCA_init();
for(int i=,a,b;i<=m;i++){
scanf("%d%d",&a,&b);
printf("%d\n",deep[LCA(vis[a],vis[b])]-);
}
return ;
}
/*问题描述
给出一些串,多组询问求两个串的最长公共前缀。字符串总长 < 10^6。
输入格式
第一行一个整数n,表示字符串的个数。
接下来n行,每行一个字符串(字符串不含空格)。
第n+2行一个整数m,表示询问总数。
接下来m行,每行两个整数a,b,表示询问第a个字符串和第b个字符串的最长公共前缀的长度。
输出格式
输出共m行,对于每个询问输出最长的公共前缀的长度
输入样例
5
abcdef
abcd
acd
cade
abcdef
4
1 2
2 3
1 5
3 4
输出样例
4
1
6
0
限制与约定
1<= m < 10^6
时间限制:1s
空间限制:256MB*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn=1e6+;
char ch[maxn];
int n,m,to[maxn][],cnt,vis[maxn],maxdeep;
//vis[i]表示字符串i在trie树中位置
inline void insert(int p){
int u=;int len=strlen(ch+);
maxdeep+=len;
for(int i=;i<=len;i++){
int v=ch[i]-'a';
if(!to[u][v])to[u][v]=++cnt;
u=to[u][v];
}
vis[p]=u;
//printf("%d%d\n",p,u);
}
int deep[maxn],f[maxn][],log[maxn];
void build(int u){
for(int i=;i<=log[ deep[u] ];i++)
f[u][i]=f[f[u][i-]][i-];
for(int i=;i<=;i++){
if(to[u][i]){
deep[ to[u][i] ]=deep[u]+;
f[ to[u][i] ][]=u; build(to[u][i]);
}
}
}
inline void LCA_init(){
for(int i=;i<=maxdeep;i++)log[i]=log[i>>]+;
deep[]=;//??非常关键的地方,以谁为根谁的深度就是1
build();
}
inline int LCA(int x,int y){
if(deep[x]<deep[y])swap(x,y);
for(int i=log[deep[x]];i>=;i--){
if(deep[y]<=deep[f[x][i]])x=f[x][i];
}
if(x==y)return x;
for(int i=log[deep[x]];i>=;i--){
if(f[x][i]!=f[y][i]){
x=f[x][i];y=f[y][i];
}
}
return f[x][];
}
int main(){
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%s",ch+);
insert(i);
}
scanf("%d",&m);
LCA_init();
for(int i=,a,b;i<=m;i++){
scanf("%d%d",&a,&b);
printf("%d\n",deep[LCA(vis[a],vis[b])]-);
}
return ;
}

用于优化dp:

/*问题描述
给出一个由S个不同的单词组成的字典和一个长字符串。把这个字符串分解成若干个单词的连接(单词可以重复使用),有多少种方法?
输入格式
输入包含多组数据。每组数据第一行为小写字母组成的待分解字符串,长度L不超过300 000,紧接着是单词个数S(1<=S<=4000)。第二行为S个单词,单词中间用空格隔开
S个单词由不超过100个小写字母组成。输入结束标志为文件结束符(EOF)。
输出格式
对于每组输出数据,输出分解方案数除以20071027的余数。
样例输入
abcd 4
a b cd ab
样例输出
Case 1: 2
样例解释:
方案1:abcd=a+b+cd; 方案二:abcd=ab+cd。
限制与约定
时间限制:1s
空间限制:128MB*/
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<cstdlib>
#include<algorithm>
using namespace std;
template<typename T>
inline void read(T &a){
a=;bool b=;char x=getchar();
while(x<''||''<x){
if(x=='-')b=;
x=getchar();
}
while(''<=x&&x<=''){
a=(a<<)+(a<<)+x-'';
x=getchar();
}
if(b)a=-a;
}
char C[];
int temp;
template<typename T>
inline void write(T a){
if(a<){
putchar('-');
a=-a;
}
do{
C[++temp]=a%+'';
a/=;
}while(a);
while(temp)putchar(C[temp--]);
}
const int maxn=4e5+;
const int P=;
int to[maxn][],cnt,f[maxn];
char target[maxn],a[maxn];
bool vis[maxn];
inline void insert(){
int u=;
for(int i=strlen(a+);i;i--){//注意倒序插入
int v=a[i]-'a';
if(!to[u][v])to[u][v]=++cnt;
u=to[u][v];
}
vis[u]=;
}
inline void query(int p){
//printf("%d:",p);
int u=;
for(int i=;i<=;i++){//找位置p及之前的99位
if(p==i)break;
int v=target[p-i]-'a';
if(!to[u][v])break; //printf("v%d ",v); u=to[u][v];
if(vis[u])f[p]=(f[p]+f[p-i-])%P;
}
//printf("\n");
}
int s;
inline void clear(){
memset(to,,sizeof(to));
cnt=;
memset(f,,sizeof(f));
memset(vis,,sizeof(vis));
}
int main(){
int tt=;
while(scanf("%s",target+)!=EOF){
tt++;
clear(); scanf("%d",&s);
while(s--){
scanf("%s",a+);
insert();
}
int len=strlen(target+);
f[]=;
for(int i=;i<=len;i++)query(i);
printf("Case %d: %d\n",tt,f[len]);
}
return ;
}

问题描述

给定 一个含N个元素的数组A,下标从1开始,请找出下面式子的最大值: (A[L1]^ A[L1+1]^…^ A[R1])+ (A[L2]^ A[L2+1]^…^ A[R2])。其中1<= L1<= R1< L2<= R2<=N。式子中x^y表示x和y的按位异或运算。

输入格式

输入数据的第一行包含一个整数N,表示数组中的元素个数。 第二行包含N个整数A1,A2,…,AN。

输出格式

输出一行包含给定表达式可能的最大值。

输入样例

5
1 2 3 1 2

输出样例

6

限制与约定

满足条件的(l1,r1,l2,r2)有:(1,2,3,3),(1,2,4,5),(3,3,4,5)。

对于100%的数据,2 ≤ N ≤ 4*10^5,0 ≤ Ai ≤ 10^9。

时间限制:1s

空间限制:256

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<vector>
using namespace std;
template<typename T>
inline void read(T &a){
a=;bool b=;char x=getchar();
while(x<''||''<x){
if(x=='-')b=;
x=getchar();
}
while(''<=x&&x<=''){
a=(a<<)+(a<<)+x-'';
x=getchar();
}
if(b)a=-a;
}
char C[];
int temp;
template<typename T>
inline void write(T a){
if(a<){
putchar('-');
a=-a;
}
do{
C[++temp]=a%+'';
a/=;
}while(a);
while(temp)putchar(C[temp--]);
}
/*
main thought:预处理出prexor[i]表示位置a[1]^……^a[i]的xor值,
以及premax[i]表示max(prexor[j]~prexor[k]),由于递增,O(n)求出
最后扫描一遍即可
*/
const int maxn=4e5+;
int to[maxn*][],cnt;//空间别算错,二叉树
inline void insert(int num){
int u=;
for(int i=;i>=;i--){//倒序插入,32~0位
bool v=num&(<<i);//取出
if(!to[u][v])to[u][v]=++cnt;
u=to[u][v];
}
}
inline int query(int num){
int ans=,u=;
for(int i=;i>=;i--){
bool v=num&(<<i);//取出该位&
if(to[u][v^]){
u=to[u][v^];
ans=(ans<<|);
}
else {
u=to[u][v];
ans<<=;
}
}
return ans;
}
int prexor[maxn],maxpre[maxn],n,a[maxn],ans,now;
inline void clear(){
memset(to,,sizeof(to));
cnt=;
}
int main(){
read(n);
insert();//记得插入0
for(int i=;i<=n;i++){
read(a[i]);
prexor[i]=prexor[i-]^a[i];
insert(prexor[i]);
maxpre[i]=max(maxpre[i-],query(prexor[i]));
}
clear();
insert();//记得插入0
for(int i=n;i;i--){
now^=a[i];
insert(now);
ans=max(ans,maxpre[i-]+query(now));
}
write(ans);
return ;
}

trie字典树:初学的更多相关文章

  1. 萌新笔记——C++里创建 Trie字典树(中文词典)(一)(插入、遍历)

    萌新做词典第一篇,做得不好,还请指正,谢谢大佬! 写了一个词典,用到了Trie字典树. 写这个词典的目的,一个是为了压缩一些数据,另一个是为了尝试搜索提示,就像在谷歌搜索的时候,打出某个关键字,会提示 ...

  2. Trie字典树 动态内存

    Trie字典树 #include "stdio.h" #include "iostream" #include "malloc.h" #in ...

  3. 算法导论:Trie字典树

    1. 概述 Trie树,又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构,如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树. Trie一词来自retrieve,发音为/tr ...

  4. 标准Trie字典树学习二:Java实现方式之一

    特别声明: 博文主要是学习过程中的知识整理,以便之后的查阅回顾.部分内容来源于网络(如有摘录未标注请指出).内容如有差错,也欢迎指正! 系列文章: 1. 标准Trie字典树学习一:原理解析 2.标准T ...

  5. 817E. Choosing The Commander trie字典树

    LINK 题意:现有3种操作 加入一个值,删除一个值,询问pi^x<k的个数 思路:很像以前lightoj上写过的01异或的字典树,用字典树维护数求异或值即可 /** @Date : 2017- ...

  6. C++里创建 Trie字典树(中文词典)(一)(插入、遍历)

    萌新做词典第一篇,做得不好,还请指正,谢谢大佬! 写了一个词典,用到了Trie字典树. 写这个词典的目的,一个是为了压缩一些数据,另一个是为了尝试搜索提示,就像在谷歌搜索的时候,打出某个关键字,会提示 ...

  7. 数据结构 -- Trie字典树

    简介 字典树:又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种. 优点:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高. 性质:   1.  根节 ...

  8. 踹树(Trie 字典树)

    Trie 字典树 ~~ 比 KMP 简单多了,无脑子选手学不会KMP,不会结论题~~ 自己懒得造图了OI WIKI 真棒 字典树大概长这么个亚子 呕吼真棒 就是将读进去的字符串根据当前的字符是什么和所 ...

  9. trie字典树详解及应用

    原文链接    http://www.cnblogs.com/freewater/archive/2012/09/11/2680480.html Trie树详解及其应用   一.知识简介        ...

  10. Trie字典树 静态内存

    静态字典树 看了好久的字典树,挺简单的一个结构,愣是看了这么久才写出来... 专心一点就不会这样了.... 接下来就去刷刷字典树的题吧....... 下面是字典树.... 定义节点 typedef s ...

随机推荐

  1. sql server查看表是否死锁

    1,查看那个表死锁 select object_name(resource_associated_entity_id) as tableName, request_session_id as pid ...

  2. MT【329】二次函数系数的最大最小

    已知二次函数$f(x)=ax^2+bx+c$有零点,且$a+b+c=1$ 若$t=\min\{a,b,c\}$求$t$的最大值. 分析:由$a,c$的对称性,不妨$c\ge a$即$2a+b\le1$ ...

  3. vue+typescript基础练习

    环境 win10 node -v 8.9.3 vue-cli 3.4 typescript 3.1.5 编辑器 vscode 目标 使用vuecli工具,建立一个项目,使用typescript.并实现 ...

  4. [ZJOI2019]线段树

    题目大意 一开始有一棵线段树,然后有一个操作序列,问执行这个操作序列的所有子集时线段树上有标记的节点个数和. 题解 其实我们把它除以\(2^m\)后发现就是有标记节点的期望个数. 然后套路的根据期望的 ...

  5. BUG-‘Tokenizer’ object has no attribute ‘oov_token’

    使用keras包实现NLP问题时,报错 /lib/python3./dist-packages/keras/preprocessing/text.py”, line , in texts_to_seq ...

  6. STL初始化initializer_list

    #include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h&g ...

  7. LA4080/UVa1416 Warfare And Logistics 最短路树

    题目大意: 求图中两两点对最短距离之和 允许你删除一条边,让你最大化删除这个边之后的图中两两点对最短距离之和. 暴力:每次枚举删除哪条边,以每个点为源点做一次最短路,复杂度\(O(NM^2logN)\ ...

  8. 给centos装图形界面 widowsx

    检查Linux系统是否能够联网.   执行命令 yum -y groupinstall Desktop   等上面的命令执行完后,再执行这条命令 yum -y groupinstall "X ...

  9. 【优秀的图片后期编辑工具】Luminar 3.1 for Mac

     [简介] 今天和大家分享最新的 Luminar for Mac 3.1 版本,支持中文界面,Luminar是一款Mac上优秀的图片后期处理工具,功能类似 Photoshop Lightroom 等软 ...

  10. Windows系统CMD下常用命令

    命令    功能ASSOC    显示或修改文件扩展名关联.ATTRIB    显示或更改文件属性.BREAK    设置或清除扩展式 CTRL+C 检查.BCDEDIT    设置启动数据库中的属性 ...