字典树专题

一、模板

数组版的:

 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 4099Revenge 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 }
View Code

2、【POJ 2513Colored Sticks

题意:输入无数根棒子,每根棒子的两端是一种颜色,问能不能将这些棒子首尾相接连成一条直线,只有颜色相同的才能接在一起 

  欧拉回路:

  图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 } 
View Code

3、【POJ 1056IMMEDIATE 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 } 
View Code

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 } 
View Code

5、【HDU 1298】T9(搜索+字典树)

题意:首先给定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 }
View Code

6、【HDU 4825】Xor Sum

题意:给你一组数,每次询问给一个数  求数组中与该数异或值最大的数。数组大小不超过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 }
View Code

 

posted @ 2016-04-24 23:02  穿破石  阅读(309)  评论(0编辑  收藏  举报