字典树专题
一、模板
数组版的:
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define maxn 500050 4 char str[50010][100]; 5 int nxt; 6 struct node 7 { 8 int val; 9 int next[26]; 10 }tree[maxn]; 11 //将结构体改成数组更容易初始化,treenext[maxn][26],treeval[maxn] 12 int add() 13 { 14 memset(&tree[nxt],0,sizeof(node)); 15 return nxt++; 16 } 17 void insert(char *s) 18 { 19 int rt=0,len=strlen(s); 20 for(int i=0;i<len;i++) 21 { 22 int c=s[i]-'a'; 23 if(!tree[rt].next[c]) 24 { 25 tree[rt].next[c]=add(); 26 } 27 rt=tree[rt].next[c]; 28 } 29 tree[rt].val++; 30 } 31 int query(char *s){ 32 int rt=0, len = strlen(s); 33 int id=-1; 34 for(int i=0; i<len; i++){ 35 int c=s[i]-'0'; 36 if(!tree[rt].next[c]) return -1; 37 rt=tree[rt].next[c]; 38 id = tree[rt].val; 39 } 40 return id; 41 } 42 int main() 43 { 44 int l=0; 45 nxt=1; 46 while(scanf("%s",str[l])!=EOF) 47 { 48 insert(str[l]); 49 l++; 50 } 51 for(int i=0;i<l;i++) 52 { 53 printf("%d\n",query(str[i])); 54 } 55 return 0; 56 }
指针版的:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 char c[100][100]; 5 const int MAX=10; 6 typedef struct Node 7 { 8 int val; 9 struct Node *next[MAX]; 10 }TrieNode; 11 12 TrieNode *head; 13 14 void insert(char str[], int id) 15 { 16 Node *t,*s=head; 17 int len=strlen(str); 18 for(int i=0;i<len;i++) 19 { 20 int c=str[i]-'0'; 21 if(s->next[c]==NULL) 22 { 23 t=new Node; 24 for(int j=0;j<10;j++) t->next[j]=NULL; 25 t->val=-1; 26 s->next[c]=t; 27 } 28 s=s->next[c]; 29 if(s->val<0) s->val=id; 30 } 31 } 32 33 int query(char str[]) 34 { 35 Node *s=head; 36 int ans; 37 int len=strlen(str); 38 for(int i=0;i<len;i++) 39 { 40 int c=str[i]-'0'; 41 if(s->next[c]==NULL) return -1; 42 s=s->next[c]; 43 ans=s->val; 44 } 45 return ans; 46 } 47 48 void Tree_Del(Node *p) 49 { 50 for(int i=0;i<10;i++) 51 { 52 if(p->next[i]!=NULL) 53 Tree_Del(p->next[i]); 54 } 55 free(p); 56 } 57 58 int main() 59 { 60 head=new Node; 61 for(int i=0;i<10;i++)head->next[i]=NULL; 62 head->val=-1; 63 int n, m; 64 char str[60]; 65 scanf("%d%d",&n, &m); 66 for(int i=0; i<n; i++) 67 { 68 scanf("%s",str); 69 insert(str, i); 70 } 71 for(int i=0; i<m; i++){ 72 scanf("%s", str); 73 printf("%d\n", query(str));//输出字符串是在第几次出现过 74 } 75 Tree_Del(head);//释放空间,防止超内存 76 return 0; 77 }
二、题目
1、【HDU 4099】Revenge of Fibonacci
题意:给出斐波那契数列的前k位,k不超过40,找出最小的正整数n,满足F(n)的前k位与给定数的前k位相同,斐波那契数列的项数不超过100000。
解题思路:本题可以分为两步:
第一步就是预处理出100000项斐波那契数列的前40位,插入到字典树中。
第二步就是查询匹配求最小的n。
对于第一步,我们可以把斐波那契数列精确到50多位,然后只存40位即可,这样就防止进位的误差。在斐波那契数列加法过程中,我们只把它的前50多位进行相加,不然存不下。
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 #include <string> 6 using namespace std; 7 const int N=1e6+10; 8 char f[5][100], str[100]; 9 void add(char *a, char *b, char *c){ 10 int lena=strlen(a), lenb=strlen(b); 11 int lenc=0, tmp=0; 12 while(1){ 13 if(lena==0 && lenb==0) break; 14 if(lena==0){ 15 int sum=b[--lenb]-'0'+tmp; 16 c[lenc++] = sum%10+'0'; 17 tmp = sum/10; 18 } 19 else{ 20 int sum=b[--lenb]-'0'+a[--lena]-'0'+tmp; 21 c[lenc++] = sum%10+'0'; 22 tmp = sum/10; 23 } 24 } 25 if(tmp) c[lenc++]=tmp+'0'; 26 strcpy(a, b); 27 for(int j=0; j<lenc; j++){ 28 b[j] = c[lenc-j-1]; 29 } 30 b[lenc]='\0'; 31 } 32 int nxt; 33 struct node{ 34 int val, next[10]; 35 }tree[7*N]; 36 int add() 37 { 38 memset(&tree[nxt],0,sizeof(node)); 39 return nxt++; 40 } 41 42 void insert(char *s, int id) 43 { 44 int rt=0,len=strlen(s); 45 for(int i=0;i<min(len, 41);i++) 46 { 47 int c=s[i]-'0'; 48 if(!tree[rt].next[c]) 49 { 50 tree[rt].next[c]=add(); 51 tree[tree[rt].next[c]].val=id; 52 } 53 rt=tree[rt].next[c]; 54 } 55 } 56 57 int query(char *s){ 58 int rt=0, len = strlen(s); 59 int id=-1; 60 for(int i=0; i<len; i++){ 61 int c=s[i]-'0'; 62 if(!tree[rt].next[c]) return -1; 63 rt=tree[rt].next[c]; 64 id = tree[rt].val; 65 } 66 return id; 67 } 68 int main(){ 69 nxt = 1; 70 f[0][0]='1', f[1][0]='1'; 71 insert(f[0], 0); 72 for(int i=2; i<100000; i++){ 73 int len=strlen(f[0]), llen=strlen(f[1]); 74 if(llen>60){ 75 f[1][llen-1]='\0'; 76 f[0][len-1]='\0'; 77 } 78 add(f[0], f[1], f[3]); 79 insert(f[1], i); 80 } 81 int t, cas=1; 82 scanf("%d", &t); 83 while(t--){ 84 scanf("%s", str); 85 int flag = query(str); 86 printf("Case #%d: %d\n", cas++, flag); 87 } 88 return 0; 89 }
题意:输入无数根棒子,每根棒子的两端是一种颜色,问能不能将这些棒子首尾相接连成一条直线,只有颜色相同的才能接在一起
欧拉回路: 图G,若存在一条路,经过G中每条边有且仅有一次,称这条路为欧拉路,如果存在一条回路经过G每条边有且仅有一次,称这条回路为欧拉回路。具有欧拉回路的图成为欧拉图。 判断欧拉路是否存在的方法 有向图:图连通,有一个顶点出度大入度1,有一个顶点入度大出度1,其余都是出度=入度。 无向图:图连通,只有两个顶点是奇数度,其余都是偶数度的。 判断欧拉回路是否存在的方法 有向图:图连通,所有的顶点出度=入度。 无向图:图连通,所有顶点都是偶数度。 程序实现一般是如下过程: 1.利用并查集判断图是否连通,即判断p[i] < 0的个数,如果大于1,说明不连通。 2.根据出度入度个数,判断是否满足要求。 3.利用dfs输出路径。 |
解题思路:【用字典树记录查询这种颜色的标号】 ==> 【用并查集判断这个图是不是连通的】 ==> 【判断是不是所有节点的度为偶数,或者是不是有且只有两个度为奇数的节点】
1 /* 2 输入无数根棒子,每根棒子的两端是一种颜色,问能不能将这些棒子首尾相接 3 连成一条直线,只有颜色相同的才能接在一起 4 本题不能用map,会超时 5 用字典树记录查询这种颜色的标号 6 用并查集判断这个图是不是连通的 7 再判断是不是所有节点的度为偶数,或者是不是有且只有两个度为奇数的节点。 8 */ 9 #include <iostream> 10 #include <algorithm> 11 #include <cstdio> 12 #include <cstring> 13 #include <string> 14 #include <vector> 15 using namespace std; 16 const int N=500010; 17 char s[21], ss[21]; 18 int fa[N], du[N], color; 19 int find(int x){ 20 21 return (fa[x]==-1?x:fa[x]=find(fa[x])); 22 //一开始这里写成了 return fa[x] = (fa[x]==x?fa[x]:find(fa[x])) wa了 23 //这一题一开始我们并不知道一共有几种不同的颜色,初始化每种颜色的父亲都是-1 24 } 25 struct node{ 26 int val; 27 bool isword; 28 struct node *next[26]; 29 }; 30 node *head; 31 int insert(char *s){ 32 int len=strlen(s); 33 node *t, *e=head; 34 for(int i=0; i<len; i++){ 35 int c=s[i]-'a'; 36 if(e->next[c]==NULL){ 37 t = new node; 38 for(int j=0; j<26; j++) t->next[j]=NULL; 39 t->val=-1; 40 e->next[c]=t; 41 } 42 e = e->next[c]; 43 } 44 if(e->val==-1) e->val=color++; 45 return e->val; 46 } 47 void del(node *p){ 48 for(int i=0; i<26; i++){ 49 if(p->next[i]!=NULL) del(p->next[i]); 50 } 51 free(p); 52 } 53 int main(){ 54 head = new node; 55 for(int i=0; i<26; i++) head->next[i]=NULL; 56 head->val=-1; 57 head->isword=false; 58 memset(fa, -1, sizeof(fa)); 59 memset(du, 0, sizeof(du)); 60 color=0; 61 while(~scanf("%s%s", s, ss)){ 62 int u=insert(s), v=insert(ss); 63 du[u]++, du[v]++; 64 int fu=find(u), fv=find(v); 65 if(fu==fv) continue; 66 fa[fv] = fu; 67 } 68 int cnt1=0, cnt2=0; 69 for(int i=0; i<color; i++){ 70 if(fa[i]==-1) cnt1++; 71 if(du[i]&1) cnt2++; 72 if(cnt1>1 || cnt2>2) break; 73 } 74 //没有小木棒输入默认是possible 75 if((cnt1==0||cnt1==1)&&(cnt2==0||cnt2==2)) printf("Possible\n"); 76 else printf("Impossible\n"); 77 del(head); 78 return 0; 79 }
3、【POJ 1056】IMMEDIATE DECODABILITY(查找一串是不是另一串的前缀,注意释放空间)
题意:连续输入几组字符串(遇到9就进行新的一组字符串),每组字符串中,要是有一个是另一个的前缀,那就输出not immediately decodable,否则就输出immediately decodable
解题思路:字典树的简单应用
注意点:每次释放树后,立马建立一个新的节点
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 #include <string> 6 using namespace std; 7 struct node{ 8 int val; 9 struct node *next[2]; 10 }; 11 node *head; 12 bool insert(char *s){ 13 int len = strlen(s), flag=0; 14 node *t, *e=head; 15 for(int i=0; i<len; i++){ 16 int c = s[i]-'0'; 17 if(e->next[c]==NULL){ 18 t = new node; 19 t->next[0]=NULL, t->next[1]=NULL; 20 t->val=-1; 21 e->next[c] = t; 22 } 23 e = e->next[c]; 24 if(e->val==1) flag=1; 25 } 26 e->val=1; 27 if(flag==0) return true; 28 else return false; 29 } 30 void del(node *p) 31 { 32 for(int i=0;i<2;i++) 33 { 34 if(p->next[i]!=NULL) del(p->next[i]); 35 } 36 free(p); 37 } 38 int main(){ 39 int cas=1; 40 head = new node; 41 head->next[0]=NULL, head->next[1]=NULL; 42 head->val=-1; 43 char str[15]; 44 int flag=0; 45 while(~scanf("%s", str)){ 46 if(strlen(str)==1 && str[0]=='9'){ 47 if(flag==0) printf("Set %d is immediately decodable\n", cas++); 48 else printf("Set %d is not immediately decodable\n", cas++); 49 del(head); 50 //立马建立跟节点,否则会出错 51 head = new node; 52 head->next[0]=NULL, head->next[1]=NULL; 53 head->val=-1; 54 flag=0; 55 continue; 56 } 57 bool k = insert(str); 58 if(!k) flag=1; 59 } 60 return 0; 61 }
4、【hdu 1247】Hat’s Words(字符串固定长度的拷贝)
题意:输入一连串的单词,按字典序输出能由其他两个单词组成的单词
解题思路:【思路1:将这些单词按照长度从小到大排序,边插入字典树边查找是否能由其它两个单词组成】 【思路2:先将所有的单词插入字典树,然后将每个单词拆分成两个单词,看看这两个单词是不是在字典树中】
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 #include <string> 6 using namespace std; 7 //string str[50100]; 8 struct Node{ 9 string str; 10 int id; 11 }p[50100]; 12 int vis[50100]; 13 string ss[50100]; 14 int lens; 15 bool cmp(Node i, Node j){ 16 return i.str.size()<j.str.size(); 17 } 18 struct node{ 19 int val; 20 struct node *next[26]; 21 }; 22 node *head; 23 bool find(string ss){ 24 int len=ss.size(); 25 node *t, *e=head; 26 bool flag=false; 27 for(int i=0; i<len; i++){ 28 int c = ss[i]-'a'; 29 if(e->next[c]==NULL) return false; 30 e = e->next[c]; 31 } 32 if(e->val==1) return true; 33 return false; 34 } 35 bool insert(string s){ 36 int len=s.size(); 37 node *t, *e=head; 38 bool flag=false; 39 for(int i=0; i<len; i++){ 40 int c = s[i]-'a'; 41 if(e->next[c]==NULL){ 42 t=new node; 43 for(int i=0; i<26; i++) t->next[i]=NULL; 44 t->val=-1; 45 e->next[c] = t; 46 } 47 e = e->next[c]; 48 if(e->val==1 && !flag) {//e->val==1表示存在从0~i这个单词,判断i+1~len-1这部分的单词是否存在 49 string ss; 50 for(int j=i+1; j<len; j++) ss+=s[j]; 51 flag=find(ss); 52 } 53 } 54 e->val=1; 55 if(flag) return true; 56 else return false; 57 } 58 int main(){ 59 memset(vis, 0, sizeof(vis)); 60 lens=0; 61 while(cin>>p[lens].str){ 62 p[lens].id=lens; 63 ss[lens]=p[lens].str;//要按字典序输出,但是由于一开始就是按照字典序输入的 ,可以记录输入的顺序,用vis[]标记这个单词是否可由两个单词组成 64 lens++; 65 } 66 sort(p, p+lens,cmp);//按照长度从小到大排序 67 head=new node; 68 for(int i=0; i<26; i++) head->next[i]=NULL; 69 head->val=-1; 70 for(int i=0; i<lens; i++){ 71 bool flag=insert(p[i].str); 72 if(flag) vis[p[i].id]=1; 73 } 74 for(int i=0; i<lens; i++){ 75 if(vis[i]) cout<<ss[i]<<endl; 76 } 77 return 0; 78 }
题意:首先给定n组字符串s和数字a,表示该字符串s输入过a次,每个字母出现的次数课叠加,接下来q次输入,每次输入表示按键顺序(使用手机输入发那种),问每次按键可能出现的字符串,按使用频率高的输出,若没有则输出MANUALLY。输入1表示开始下一个单词
解题思路:先将字母出现的频数用字典树记录,边搜边找
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 #include <string> 6 using namespace std; 7 const string str[8]={"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; 8 struct node{ 9 int val; 10 struct node *next[26]; 11 }; 12 node *head; 13 14 void insert(string s, int id){ 15 node *t, *e=head; 16 for(int i=0; i<(int)s.size(); i++){ 17 int c=s[i]-'a'; 18 if(e->next[c]==NULL){ 19 t = new node; 20 for(int j=0; j<26; j++) t->next[j]=NULL; 21 t->val=0; 22 e->next[c]=t; 23 } 24 e = e->next[c]; 25 e->val += id; 26 } 27 } 28 int maxn; 29 string ssl; 30 void dfs(string s, int li, node *t, string sl){ 31 if((int)s.size()==li) { 32 if(t->val > maxn) { 33 maxn = t->val; 34 ssl=sl; 35 } 36 return; 37 } 38 int k=s[li]-'0'-2; 39 for(int i=0; i<(int)str[k].size(); i++){ 40 int c = str[k][i]-'a'; 41 if(t->next[c]==NULL) continue; 42 dfs(s, li+1, t->next[c], sl+str[k][i]); 43 } 44 } 45 void del(node *p){ 46 for(int i=0; i<26; i++){ 47 if(p->next[i]!=NULL) del(p->next[i]); 48 } 49 free(p); 50 } 51 int main(){ 52 int t; 53 scanf("%d", &t); 54 int cas=1; 55 while(t--){ 56 head = new node; 57 for(int i=0; i<26; i++) head->next[i]=NULL; 58 head->val=-1; 59 int n, m; 60 scanf("%d", &n); 61 string ss; 62 int id; 63 for(int i=0; i<n; i++){ 64 cin>>ss>>id; 65 insert(ss, id); 66 } 67 scanf("%d", &m); 68 printf("Scenario #%d:\n", cas++); 69 string si, sl; 70 for(int i=0; i<m; i++){ 71 cin>>ss; 72 si.clear(); 73 for(int j=0; j<(int)ss.size()-1; j++){ 74 si+=ss[j]; 75 sl.clear(); 76 ssl.clear(), maxn=-1; 77 dfs(si, 0, head, sl); 78 if(maxn==-1) cout<<"MANUALLY"<<endl; 79 else cout<<ssl<<endl; 80 } 81 cout<<endl; 82 } 83 cout<<endl; 84 del(head); 85 } 86 return 0; 87 }
题意:给你一组数,每次询问给一个数 求数组中与该数异或值最大的数。数组大小不超过10^5,数组中的每个数都是32位的
解题思路:根据异或的特点,要想得到的异或值最大,尽可能的让两个数的每位都相反,先把给定的一组数建树,数的最后一位对应的节点保存这个数的位置(放便取) 对于每个询问,在搜树时优先考虑和当前数位相反的节点。
关键:【for(int i=32; i>=0; i--) int k = ((1LL<<i)&x)>0;】
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 #include <string> 6 using namespace std; 7 const int N=1e5+10; 8 struct node{ 9 int val; 10 struct node *next[2]; 11 }; 12 node *head; 13 int a[N]; 14 15 void insert(int x, int id){ 16 node *t, *e=head; 17 for(int i=32; i>=0; i--){ 18 int k = ((1LL<<i)&x)>0; 19 if(e->next[k]==NULL){ 20 t = new node; 21 t->val=-1; 22 t->next[0]=NULL, t->next[1]=NULL; 23 e->next[k]=t; 24 } 25 e = e->next[k]; 26 } 27 e->val=id; 28 } 29 int find(int x){ 30 node *t, *e=head; 31 for(int i=32; i>=0; i--){ 32 int k = ((1LL<<i)&x)>0; 33 k = 1-k; 34 if(e->next[k] == NULL) k=1-k; 35 e = e->next[k]; 36 } 37 int k=e->val; 38 return a[k]; 39 } 40 void del(node *p){ 41 for(int i=0; i<2; i++){ 42 if(p->next[i] != NULL) del(p->next[i]); 43 } 44 free(p); 45 } 46 int main(){ 47 int t, cas=1; 48 scanf("%d", &t); 49 while(t--){ 50 head = new node; 51 head->val=-1; 52 head->next[0] = NULL; 53 head->next[1] = NULL; 54 int xi, n, m; 55 scanf("%d%d", &n, &m); 56 for(int i=0; i<n; i++){ 57 scanf("%d", &a[i]); 58 insert(a[i], i); 59 } 60 printf("Case #%d:\n", cas++); 61 for(int i=0; i<m; i++){ 62 scanf("%d", &xi); 63 printf("%d\n", find(xi)); 64 } 65 del(head); 66 } 67 return 0; 68 }