字典树&&01字典树专题&&对字典树的理解
对于字典树和01字典树的一点理解:
首先,字典树建树的过程就是按照每个数的前缀来的,如果你要存储一个全小写字母字符串,那么这个树每一个节点最多26个节点,这样的话,如果要找特定的单词的话,按照建树的方式找就可以了。
然后是01字典树,这个树在处理一些异或问题的时候特别好用,首先在存储一个树的过程中,我们是按照从高位开始的,如果是对于int型的,我们就从这个数的32位开始存储,不够的话,按照0补,这是建树的过程。再就是查询的时候,对于给定的数,我们先去找这一位上和他不同的,比如说,如果当前这个数的第i位上是1,那我们就找有没有一个数第i位上是0,如果没有的话,再去找第i位是0的数,然后按照从高位到低位寻找的话,就一定能寻找到满足情况的最优解。
入门:
题目链接:http://acm.sdut.edu.cn/onlinejudge2/index.php/Home/Index/problemdetail/pid/2828.html
用数组模拟。
我的理解:假设给你n个字符串,我们可以把有相同前缀的按照树的形式存储下来,这样就能够节省很多的空间,然后通过递归的形式来建树或者查找子串是否存在或者存在的次数。
AC代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn = 1e6+10; 4 char tmp[maxn]; 5 int root[maxn][30]; 6 int top; 7 void init() 8 { 9 for(int i=0; i<top; i++) 10 { 11 for(int j=0; j<30; j++) 12 { 13 root[i][j]=0; 14 } 15 } 16 top=0; 17 } 18 void add(char *str) 19 { 20 int tot=0; 21 int len=strlen(str); 22 for(int i=0; i<len; i++) 23 { 24 int t=str[i]-'a'; 25 if(root[tot][t]==0) 26 root[tot][t]=++top; 27 tot=root[tot][t]; 28 } 29 } 30 bool judge(char *str) 31 { 32 int len=strlen(str); 33 int tot=0; 34 for(int i=0; i<len; i++) 35 { 36 int t=str[i]-'a'; 37 if(root[tot][t]==0) 38 return false; 39 tot=root[tot][t]; 40 } 41 return true; 42 } 43 int main() 44 { 45 int n,m; 46 top=0; 47 while(~scanf("%d %d",&n,&m)&&(n+m)) 48 { 49 init(); 50 for(int i=1; i<=n; i++) 51 { 52 // getchar(); 53 scanf("%s",tmp); 54 add(tmp); 55 // cout<<tmp<<endl; 56 } 57 for(int i=1; i<=m; i++) 58 { 59 // getchar(); 60 scanf("%s",tmp); 61 if(judge(tmp)) 62 printf("Yes\n"); 63 else 64 printf("No\n"); 65 } 66 } 67 return 0; 68 }
A题:
题目链接:https://cn.vjudge.net/contest/276901#problem/A
题目大意:先输入若干个字符串,然后再每一次输入一个字符串,问你当前这个字符串再一开始输入的字符串中是前缀的有多少个?
AC代码:
1 #include<iostream> 2 #include<stack> 3 #include<string> 4 #include<cstring> 5 #include<stdio.h> 6 using namespace std; 7 # define ll long long 8 const int maxn = 2e6+100; 9 int rec[maxn][30]; 10 int flag[maxn]; 11 int tot; 12 char str1[maxn]; 13 void add(char *str) 14 { 15 int root=0; 16 int len=strlen(str); 17 for(int i=0; i<len; i++) 18 { 19 int t=str[i]-'a'; 20 if(!rec[root][t]) 21 rec[root][t]=++tot; 22 flag[rec[root][t]]++; 23 root=rec[root][t]; 24 25 } 26 } 27 int judge(char *str) 28 { 29 int root=0; 30 int len=strlen(str); 31 for(int i=0; i<len; i++) 32 { 33 int t=str[i]-'a'; 34 if(rec[root][t]==0) 35 return 0; 36 root=rec[root][t]; 37 //cout<<i<<endl; 38 } 39 return flag[root]; 40 } 41 void init() 42 { 43 for(int i=0; i<tot; i++) 44 { 45 for(int j=0; j<30; j++) 46 { 47 rec[i][j]=0; 48 } 49 } 50 tot=0; 51 } 52 int main() 53 { 54 tot=0; 55 while(gets(str1)) 56 { 57 if(str1[0]=='\0') break; 58 add(str1); 59 } 60 while(scanf("%s",str1)!=EOF) 61 { 62 printf("%d\n",judge(str1)); 63 } 64 return 0; 65 } 66 // init(); 67 // while(gets(str1)) 68 // { 69 // if(str1[0]=='\0') 70 // break; 71 // scanf("%s",str1); 72 // add(str1); 73 // getchar(); 74 // } 75 // while(scanf("%s",str1)!=EOF) 76 // { 77 // int ans=judge(str1); 78 // //cout<<str1<<endl; 79 // printf("%d\n",ans); 80 // } 81 // return 0; 82 //}
B题:
分割字符串
版本一:使用函数 stringstream (头文件<sstream>)
https://blog.csdn.net/weixin_35929051/article/details/52502486?tdsourcetag=s_pcqq_aiomsg(stringstream的使用说明)
#include<iostream> #include<stack> #include<string> #include<cstring> #include<sstream> #include<stdio.h> using namespace std; # define ll long long const int maxn = 1e6+100; int rec[maxn][30]; int flag[maxn]; int tot,ans; string tmp,str1; void add(string str) { int root=0; int len=str.size(); for(int i=0; i<len; i++) { int t=str[i]-'a'; if(!rec[root][t]) rec[root][t]=++tot; root=rec[root][t]; } if(flag[root]==0) ans++; flag[root]=1; } void init() { // memset(flag,0,sizeof(flag)); for(int i=0; i<=tot; i++) { flag[i]=0; for(int j=0; j<30; j++) { rec[i][j]=0; } } tot=0,ans=0; } int main() { while(getline(cin,str1)) { //cout<<str1<<endl; init(); int p=0,t=0; if(str1=="#") break; stringstream ss(str1); while(ss>>str1) { add(str1); } cout<<ans<<endl; // cout<<tot<<endl; } return 0; }
版本二:(超时)
#include<iostream> #include<stack> #include<string> #include<cstring> #include<stdio.h> using namespace std; # define ll long long const int maxn = 1e6+100; int rec[maxn][30]; int flag[maxn]; int tot,ans; char tmp[maxn],str1[maxn]; void add(char *str) { int root=0; int len=strlen(str); for(int i=0; i<len; i++) { int t=str[i]-'a'; if(!rec[root][t]) rec[root][t]=++tot; root=rec[root][t]; } if(flag[root]==0)ans++; flag[root]=1; } void init() { for(int i=0; i<=tot; i++) { flag[i]=0; for(int j=0; j<30; j++) { rec[i][j]=0; } } tot=0,ans=0; } int main() { tot=0; while(gets(str1)) { init(); int p=0,t=0; if(str1[0]=='#') break; int len=strlen(str1); while(p<len) { while(str1[p]!=' '&&p<len) { tmp[t++]=str1[p]; p++; } while(str1[p]==' '&&p<len) p++; add(tmp); t=0; } printf("%d\n",ans); getchar(); } return 0; }
01字典树:
首先输入n个数,然后每次输入一个数k,寻找输入的这个数和前面的n个数中的某一个最大的异或值?也就是k^s最大?
1 #include <iostream> 2 #include <string> 3 #include <deque> 4 #include <stack> 5 #include<cmath> 6 #include <algorithm> 7 #include<map> 8 using namespace std; 9 # define ll long long 10 # define inf 0x3f3f3f3f 11 const int maxn = 3e6+100; 12 ll flag[maxn]; 13 int tot; 14 int sto[maxn][4]; 15 void init() 16 { 17 for(int i=0; i<=tot; i++) 18 { 19 flag[i]=0; 20 for(int j=0; j<3; j++) 21 { 22 sto[i][j]=0; 23 } 24 } 25 } 26 void add(ll t) 27 { 28 ll u=0; 29 for(ll i=32; i>=0; i--) 30 { 31 ll tmp=(t>>i)&1; 32 if(sto[u][tmp]==0) 33 sto[u][tmp]=++tot; 34 u=sto[u][tmp]; 35 } 36 flag[u]=t; 37 } 38 ll query(ll t) 39 { 40 ll u=0; 41 for(ll i=32; i>=0; i--) 42 { 43 ll tmp=(t>>i)&1; 44 if(sto[u][tmp^1]) 45 u=sto[u][tmp^1]; 46 else 47 u=sto[u][tmp]; 48 } 49 return flag[u]; 50 } 51 int main() 52 { 53 int T; 54 scanf("%d",&T); 55 tot=0; 56 int Case=0; 57 while(T--) 58 { 59 init(); 60 int n,m; 61 scanf("%d %d",&n,&m); 62 int tmp; 63 for(int i=1; i<=n; i++) 64 { 65 scanf("%d",&tmp); 66 add(tmp); 67 } 68 printf("Case #%d:\n",++Case); 69 for(int i=1; i<=m; i++) 70 { 71 scanf("%d",&tmp); 72 int ans=query(tmp); 73 printf("%d\n",ans); 74 } 75 } 76 return 0; 77 }
01字典树
具体思路:题目中说的是找三个不同的i,j,k,使得(a[i]+a[j])^a[k]的值最大,那么思路来了,首先第一步,我们先把所有的值全部存到字典树里面,然后两个for循环,每一次先把a[i]和a[j]去掉,然后再去求(a[i]+a[j])在字典树中的最值就可以了。
AC代码:
1 #include <iostream> 2 #include <string> 3 #include <deque> 4 #include <stack> 5 #include<cmath> 6 #include <algorithm> 7 #include<map> 8 using namespace std; 9 # define ll long long 10 # define inf 0x3f3f3f3f 11 # define ll_inf 1ll<<60 12 const int maxn = 1e6+100; 13 ll sto[maxn][3]; 14 ll flag[maxn]; 15 ll a[maxn],tot; 16 ll com[maxn]; 17 ll Max(ll t1,ll t2) 18 { 19 if(t1<t2) 20 return t2; 21 return t1; 22 } 23 void init() 24 { 25 for(int i=0; i<=tot; i++) 26 { 27 flag[i]=0; 28 com[i]=0; 29 for(int j=0; j<3; j++) 30 { 31 sto[i][j]=0; 32 } 33 } 34 tot=0; 35 } 36 void add(ll t) 37 { 38 int u=0; 39 for(int i=32; i>=0; i--) 40 { 41 int tmp=(t>>i)&1; 42 if(sto[u][tmp]==0) 43 sto[u][tmp]=++tot; 44 flag[sto[u][tmp]]++; 45 u=sto[u][tmp]; 46 } 47 com[u]=t;//存储这一位上是哪个数 48 } 49 void Erase(ll t) 50 { 51 int u=0; 52 for(int i=32; i>=0; i--) 53 { 54 int tmp=(t>>i)&1; 55 flag[sto[u][tmp]]--;//把这个数的路径走过的减去1,就相当于把这个数从树上去掉。 56 u=sto[u][tmp]; 57 } 58 // com[u]=0; 59 } 60 ll query(ll t) 61 { 62 int u=0; 63 for(int i=32; i>=0; i--) 64 { 65 int tmp=(t>>i)&1; 66 if(sto[u][tmp^1]>0)//看一下当前这一位上是不是有不一样的 67 { 68 if(flag[sto[u][tmp^1]]>0)//如果有,就按照不同进行 69 u=sto[u][tmp^1]; 70 else// 没有的话,就按照另外一种来进行 71 u=sto[u][tmp]; 72 } 73 else 74 { 75 if(flag[sto[u][tmp]]>0)//同理 76 u=sto[u][tmp]; 77 else 78 u=sto[u][tmp^1]; 79 } 80 } 81 return com[u]^t; 82 } 83 int main() 84 { 85 int T; 86 tot=0; 87 scanf("%d",&T); 88 while(T--) 89 { 90 init(); 91 int n; 92 scanf("%d",&n); 93 for(int i=1; i<=n; i++) 94 { 95 scanf("%lld",&a[i]); 96 add(a[i]); 97 } 98 ll maxx=0; 99 for(int i=1; i<=n; i++) 100 { 101 Erase(a[i]); 102 for(int j=i+1; j<=n; j++) 103 { 104 Erase(a[j]); 105 maxx=Max(maxx,query(a[i]+a[j])); 106 add(a[j]); 107 } 108 add(a[i]); 109 } 110 printf("%lld\n",maxx); 111 } 112 return 0; 113 }