刷的一套字典树的题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=120748#overview

 

  个人喜欢指针的字典树写法,但是大力喜欢数组的写法,反正是一个队的,互补一下反而更好- 。-本来前几题我的指针写法都是用new的,后来发现用new可能会超时,所以不如先静态的分配好足够的内存。因此,这样就需要和数组写法一样算节点数目了。引用一下铭神的原话:“节点数在最坏情况下可以认为是字符数量”,也就是说,节点大小应开单词数量乘以每个单词的长度上限。

  A题,赤裸裸的字典树,直接给代码,但是不好的是,这题用的是new的方法创建新节点,要作模板的话不妨使用后面几题的代码。代码如下:

 1 #include <stdio.h>
 2 #include <algorithm>
 3 #include <set>
 4 #include <math.h>
 5 #include <vector>
 6 #include <stack>
 7 #include <map>
 8 #include <string.h>
 9 #define t_mid (l+r>>1)
10 #define ls (o<<1)
11 #define rs (o<<1 | 1)
12 #define lson ls,l,t_mid
13 #define rson rs,t_mid+1,r
14 using namespace std;
15 typedef long long ll;
16 
17 struct node
18 {
19     int cnt;
20     node *child[26];
21     node()
22     {
23         cnt=0;
24         for(int i=0;i<26;i++) child[i]=NULL;
25     }
26 };
27 
28 node *root = new node();
29 
30 void Insert(char *s)
31 {
32     node *p = root;
33     int len = strlen(s);
34     for(int i=0;i<len;i++)
35     {
36         int m = s[i] - 'a';
37         if(p->child[m] != NULL)
38         {
39             p = p->child[m];
40             p->cnt++;
41         }
42         else
43         {
44             node *newnode = new node();
45             p->child[m] = newnode;
46             p = newnode;
47             p->cnt++;
48         }
49     }
50 }
51 
52 int Search(char *s)
53 {
54     int len = strlen(s);
55     node *p = root;
56     for(int i=0;i<len;i++)
57     {
58         int m = s[i] - 'a';
59         if(p->child[m] == NULL)
60         {
61             return 0;
62         }
63         else p = p->child[m];
64     }
65     return p->cnt;
66 }
67 
68 int main()
69 {
70     char s[15];
71     while(gets(s) != NULL && strcmp(s,""))
72     {
73         Insert(s);
74     }
75     while(gets(s) != NULL)
76     {
77         printf("%d\n",Search(s));
78     }
79     return 0;
80 }
View Code

  B题,把数字化成二进制,然后从高位到低位进行储存,然后匹配的时候,因为是异或,所以用相反的数字匹配就行(1的话匹配字典树里面0的路径,反之亦然)。

这题的代码一开始不知道怎么下手,还是借鉴了别人的代码的= =。代码如下:

 1 #include <stdio.h>
 2 #include <algorithm>
 3 #include <string.h>
 4 using namespace std;
 5 typedef long long ll;
 6 
 7 const int N = 34;
 8 const int M = N * 1e5;
 9 int n,m,p,root;
10 ll a[N];
11 
12 struct Node
13 {
14     int l,r;
15     int val;
16     void clear()
17     {
18         l=r=-1;
19     }
20 }node[M];
21 
22 void Insert(int &root,int deep,ll x)
23 {
24     if(root == -1)
25     {
26         root = p++;
27         node[root].clear();
28     }
29 
30     if(deep == -1) {node[root].val = x;return;}
31 
32     if(x & a[deep]) Insert(node[root].r,deep-1,x); //如果这个数的当前位是1,则向右边走
33     else Insert(node[root].l,deep-1,x);
34 }
35 
36 void Query(int root,int deep,ll x)
37 {
38     if(deep == -1)
39     {
40         printf("%d\n",node[root].val);
41         return;
42     }
43 
44     if((x & a[deep]) && node[root].l != -1 || node[root].r == -1)
45     {
46         Query(node[root].l,deep-1,x);
47     }
48     else
49     {
50         Query(node[root].r,deep-1,x);
51     }
52 }
53 
54 int main()
55 {
56     int T,cnt=1;
57     scanf("%d",&T);
58     a[0]=1;
59     for(int i=1;i<N;i++) a[i]=a[i-1]<<1;
60 
61     while(T--)
62     {
63         printf("Case #%d:\n",cnt++);
64         root = -1,p=0;
65         scanf("%d%d",&n,&m);
66         for(int i=1;i<=n;i++)
67         {
68             int t;
69             scanf("%d",&t);
70             Insert(root,N,(ll)t);
71         }
72 
73         while(m--)
74         {
75             int t;
76             scanf("%d",&t);
77             Query(0,N,(ll)t);
78         }
79     }
80 }
View Code

  C题,和B题很像,题意是任意从字典树中取出两个元素,求它们的和和剩下的元素中任意一个进行异或,得到的最大的答案是多少。一开始看了一个人的博客,那个人写的很烦,以为是难题,但是后来参考了另外一个人的代码,发现就是B题。思路很简单,枚举取出的元素,然后将这两个元素从字典树中删除!然后进行匹配,匹配完以后加回去即可。这样,删除操作其实就是Insert操作中,把cnt+1改成-1即可,所以可以把Insert改成update就可以实现两个功能了,具体见代码:

  1 #include <stdio.h>
  2 #include <algorithm>
  3 #include <string.h>
  4 using namespace std;
  5 typedef long long ll;
  6 
  7 const int N = 34;
  8 int num[1000+5];
  9 
 10 struct node
 11 {
 12     int val;
 13     int cnt;
 14     node *child[2];
 15     node()
 16     {
 17         cnt=0;
 18         val = -1;
 19         for(int i = 0 ; i < 2 ; i ++)
 20         {
 21             child[i] = NULL;
 22         }
 23     }
 24 };
 25 
 26 node *root;
 27 
 28 void Freedom(node *p)
 29 {
 30     for(int i=0 ; i < 2 ; i ++)
 31     {
 32         if(p->child[i] != NULL) Freedom(p->child[i]);
 33     }
 34 
 35     delete p;
 36 }
 37 
 38 void update(node *p,int deep,ll x,int op)
 39 {
 40     if(deep == -1) {p->val = x;return;}
 41 
 42     int m = ( (x>>deep) & 1 );
 43     if(p->child[m] == NULL)
 44     {
 45         node *newnode = new node();
 46         p->child[m] = newnode;
 47     }
 48 
 49     p->child[m]->cnt += op;
 50     update(p->child[m],deep-1,x,op);
 51 }
 52 
 53 int Query(node *p,int deep,ll x)
 54 {
 55     if(deep == -1) return (int)p->val;
 56 
 57     int m = 1 - ( (x>>deep) & 1 ) ;
 58     if(p->child[m] != NULL && p->child[m]->cnt != 0)
 59     {
 60         return Query(p->child[m],deep-1,x);
 61     }
 62     else
 63     {
 64         return Query(p->child[1-m],deep-1,x);
 65     }
 66 }
 67 
 68 int main()
 69 {
 70     int T,cnt=1;
 71     scanf("%d",&T);
 72 
 73     while(T--)
 74     {
 75         root = new node();
 76 
 77         int n;
 78         scanf("%d",&n);
 79         for(int i=1;i<=n;i++)
 80         {
 81             scanf("%d",num+i);
 82             update(root,N,(ll)num[i],1);
 83         }
 84 
 85 
 86         int ans=0;
 87         for(int i=1;i<=n;i++)
 88         {
 89             for(int j=i+1;j<=n;j++)
 90             {
 91                 int t1 = num[i] + num[j];
 92 
 93                 update(root,N,num[i],-1); //从字典树里删除这两个数
 94                 update(root,N,num[j],-1);
 95 
 96                 int t2 = Query(root,N,(ll)t1); //查找最大的值
 97 
 98                 ans = max(ans,t1^t2);
 99 
100                 update(root,N,num[i],1);  //将这两个数再次放回字典树里面
101                 update(root,N,num[j],1);
102 
103             }
104         }
105 
106         printf("%d\n",ans);
107 
108         Freedom(root); //释放内存
109     }
110 }
View Code

同时,从这题中,还有一个对new出来的节点进行delete的操作,要释放内存。所以以后干脆就不要写new的字典树写法好了。。

 

  D题,给出一堆的单词,找出每个单词的最短缩写,要求是每个缩写必须能认出这个单词,打个比方,app和apple,那么app作为app的缩写以后,apple的缩写就必须为appl。那么思路就很简单了:对一个单词,不断地匹配,直到一个节点,这个节点的cnt值为1或者这个点已经是这个单词的最后一个字母了,那么到此为止了。可以从上面这两个单词中很好的理解。虽然这题是我独立做出来的,但是查询操作还是写麻烦了,其实不需要用队列来装,只要该节点的cnt值大于1,就输出这个字母即可,直到为1或者单词结束为止。具体见代码:

 1 #include <stdio.h>
 2 #include <algorithm>
 3 #include <string.h>
 4 #include <queue>
 5 using namespace std;
 6 typedef long long ll;
 7 
 8 int num[1000+5];
 9 char s[1000+5][25];
10 
11 struct node
12 {
13     int cnt;
14     node *child[26];
15     node()
16     {
17         cnt=0;
18         for(int i = 0 ; i < 26 ; i ++)
19         {
20             child[i] = NULL;
21         }
22     }
23 };
24 
25 node *root;
26 
27 void Freedom(node *p)
28 {
29     for(int i=0 ; i < 26 ; i ++)
30     {
31         if(p->child[i] != NULL) Freedom(p->child[i]);
32     }
33 
34     delete p;
35 }
36 
37 void Insert(char *s)
38 {
39     node *p = root;
40     for(int i=0;s[i];i++)
41     {
42         int m = s[i] - 'a';
43         if(p->child[m] == NULL)
44         {
45             p->child[m] = new node();
46         }
47         p = p->child[m];
48         p->cnt++;
49     }
50 }
51 
52 void Query(char *s)
53 {
54     node *p = root;
55     queue<char> Q;
56     for(int i=0;s[i];i++)
57     {
58         int m = s[i] - 'a';
59         if(p->child[m]->cnt == 1)
60         {
61             while(!Q.empty())
62             {
63                 char c = Q.front();Q.pop();
64                 putchar(c);
65             }
66             printf("%c\n",s[i]);
67             return;
68         }
69         else
70         {
71             Q.push(s[i]);
72         }
73         p = p->child[m];
74     }
75 
76     while(!Q.empty())
77     {
78         char c = Q.front();Q.pop();
79         putchar(c);
80     }
81     puts("");
82 }
83 
84 int main()
85 {
86     int cnt = 0;
87     root = new node();
88     while(gets(s[cnt++]) != NULL)
89     {
90         Insert(s[cnt-1]);
91         //if(cnt==12) break;
92     }
93 
94     for(int i=0;i<cnt;i++)
95     {
96         printf("%s ",s[i]);
97         Query(s[i]);
98     }
99 }
View Code

  E题的意思,给出一堆的字符串,如果有一个字符串是另外一个的前缀,那么输出NO,否则输出YES。思路和上题类似,枚举所有字符串,一直到结尾为止,如果结尾那个字母出现的次数大于1,那么这个字符串肯定是另外一个字符串的前缀了。具体见代码:

 1 #include <stdio.h>
 2 #include <algorithm>
 3 #include <string.h>
 4 #include <queue>
 5 using namespace std;
 6 typedef long long ll;
 7 
 8 int n,tot=0;
 9 char s[10000+5][15];
10 
11 struct node
12 {
13     int cnt;
14     node *child[10];
15     void clear()
16     {
17         cnt=0;
18         for(int i = 0 ; i < 10 ; i ++)
19         {
20             child[i] = NULL;
21         }
22     }
23 }N[100000+5];
24 
25 node *root;
26 
27 node *newnode()
28 {
29     node *p = &N[tot++];
30     p->clear();
31     return p;
32 }
33 
34 void Freedom(node *p)
35 {
36     for(int i=0 ; i < 10 ; i ++)
37     {
38         if(p->child[i] != NULL) Freedom(p->child[i]);
39     }
40 
41     delete p;
42 }
43 
44 void Insert(char *s)
45 {
46     node *p = root;
47     for(int i=0;s[i];i++)
48     {
49         int m = s[i] - '0';
50         if(p->child[m] == NULL)
51         {
52             p->child[m] = newnode();
53         }
54         p = p->child[m];
55         p->cnt++;
56     }
57 }
58 
59 bool Isok(char *s)
60 {
61     node *p = root;
62     for(int i=0;s[i];i++)
63     {
64         int m = s[i] - '0';
65         p = p->child[m];
66     }
67     if(p->cnt == 1) return true;
68     else return false;
69 }
70 
71 int main()
72 {
73     int T;
74     scanf("%d",&T);
75     while(T--)
76     {
77         tot=0;
78         root = newnode();
79         scanf("%d",&n);
80         for(int i=1;i<=n;i++)
81         {
82             scanf("%s",s[i]);
83             Insert(s[i]);
84         }
85 
86         int flag=1;
87         for(int i=1;i<=n;i++)
88         {
89             if(!Isok(s[i])) {flag=0;break;}
90         }
91         if(!flag) puts("NO");
92         else puts("YES");
93     }
94 }
View Code

  F题,意思是找出字典中的所有串,这个串是由字典中的另外两个串拼成的。第一次做时,n^2拼接然后查找,结果当然是TLE,GG。。然后用了大力的方法:比方说三个单词,abc,qwe,abcqwe。查找abcqwe的时候,到c时,c是某个单词(abc)的结尾并且c的下一个字母并不是abcqwe的结尾,让下一个字母(q)入栈,到时候取出栈内所有元素,然后对每个元素进行继续匹配,如果到最后一个字母时这个字母是另外一个单词的结尾,那么这个单词(abcqwe)就是满足题意的。。这题还是有一点技巧性的,比方说栈内存的是那个字母的位置而不是char。关于判断是不是另外单词的结尾,只要在节点处在设置一个布尔类型的变量ised即可。具体见代码吧:

  1 #include <stdio.h>
  2 #include <algorithm>
  3 #include <string.h>
  4 #include <queue>
  5 #include <stack>
  6 using namespace std;
  7 typedef long long ll;
  8 
  9 int n,tot=0;
 10 char s[50000+5][15];
 11 
 12 struct node
 13 {
 14     int cnt;
 15     bool ised;
 16     node *child[26];
 17     void clear()
 18     {
 19         cnt = 0;
 20         ised = false;
 21         for(int i = 0 ; i < 26 ; i ++)
 22         {
 23             child[i] = NULL;
 24         }
 25     }
 26 }N[20*(50000+5)];
 27 
 28 node *root;
 29 
 30 node *newnode()
 31 {
 32     node *p = &N[tot++];
 33     p->clear();
 34     return p;
 35 }
 36 
 37 void Freedom(node *p)
 38 {
 39     for(int i=0 ; i < 26 ; i ++)
 40     {
 41         if(p->child[i] != NULL) Freedom(p->child[i]);
 42     }
 43 
 44     delete p;
 45 }
 46 
 47 void Insert(char *s)
 48 {
 49     node *p = root;
 50     for(int i=0;s[i];i++)
 51     {
 52         int m = s[i] - 'a';
 53         if(p->child[m] == NULL)
 54         {
 55             p->child[m] = newnode();
 56         }
 57         p = p->child[m];
 58         p->cnt++;
 59     }
 60     p->ised = true;
 61 }
 62 
 63 bool Query(char *s)
 64 {
 65     stack<int> S;
 66     node *p = root;
 67     for(int i=0;s[i];i++)
 68     {
 69         int m = s[i] - 'a';
 70         p = p->child[m];
 71         if(p->ised == true && s[i+1]) S.push(i+1);
 72     }
 73 
 74     while(!S.empty())
 75     {
 76         p = root;
 77         int x = S.top();S.pop();
 78         for(int i=x;s[i];i++)
 79         {
 80             int m = s[i] - 'a';
 81             if(p->child[m] == NULL) break;
 82             else p = p->child[m];
 83             if(!s[i+1] && p->ised == true) return true;
 84         }
 85     }
 86     return false;
 87 }
 88 
 89 int main()
 90 {
 91     int cnt = 0;
 92     tot = 0;
 93     root = newnode();
 94     while(gets(s[cnt++]) != NULL)
 95     {
 96         Insert(s[cnt-1]);
 97         //if(cnt == 6) break;
 98     }
 99 
100     for(int i=0;i<cnt;i++)
101     {
102         if(Query(s[i])) puts(s[i]);
103     }
104 }
View Code

  最后一题也是很吊的一题。题意是模仿手机上的九格输入法,输入了一系列的单词。然后给你一些数字,表示现在的输入,问每次输入一个数字,按照频率,最可能是在输入什么内容。。具体的题意还是看原题吧。然后思路就是同样的匹配,把每一个数字当做一个状态,对这个状态进行若干个方向的选择,选择频率最大的内容即可。具体见代码吧:

  1 #include <stdio.h>
  2 #include <algorithm>
  3 #include <string.h>
  4 #include <queue>
  5 #include <stack>
  6 #include <string>
  7 #include<iostream>
  8 using namespace std;
  9 typedef long long ll;
 10 
 11 int n,tot=0,fre;
 12 string ans;
 13 char str[100+5];
 14 int cnt[10] = {0,0,3,3,3,3,3,4,3,4};
 15 char ch[10][5] = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
 16 
 17 struct node
 18 {
 19     int cnt;
 20     node *child[26];
 21     void clear()
 22     {
 23         cnt = 0;
 24         for(int i = 0 ; i < 26 ; i ++)
 25         {
 26             child[i] = NULL;
 27         }
 28     }
 29 }N[100000+5];
 30 
 31 node *root;
 32 
 33 node *newnode()
 34 {
 35     node *p = &N[tot++];
 36     p->clear();
 37     return p;
 38 }
 39 
 40 void Freedom(node *p)
 41 {
 42     for(int i=0 ; i < 26 ; i ++)
 43     {
 44         if(p->child[i] != NULL) Freedom(p->child[i]);
 45     }
 46 
 47     delete p;
 48 }
 49 
 50 void Insert(char *s,int t)
 51 {
 52     node *p = root;
 53     for(int i=0;s[i];i++)
 54     {
 55         int m = s[i] - 'a';
 56         if(p->child[m] == NULL)
 57         {
 58             p->child[m] = newnode();
 59         }
 60         p = p->child[m];
 61         p->cnt += t;
 62     }
 63 }
 64 
 65 void Query(int st,int ed,node *p,string s)
 66 {
 67     if(st == ed)
 68     {
 69         if(p->cnt > fre)
 70         {
 71             fre = p->cnt;
 72             ans = s;
 73         }
 74     }
 75 
 76     int num = str[st] - '0';
 77     for(int i=0;i<cnt[num];i++)
 78     {
 79         //对这个键的各个方向进行试探性的选择
 80         int m = ch[num][i] - 'a';
 81         if(p->child[m] != NULL) Query(st+1,ed,p->child[m],s+ch[num][i]);
 82     }
 83 }
 84 
 85 int main()
 86 {
 87     int T;
 88     scanf("%d",&T);
 89     for(int kase=1;kase<=T;kase++)
 90     {
 91         tot = 0;
 92         root = newnode();
 93         printf("Scenario #%d:\n",kase);
 94         int n;
 95         scanf("%d",&n);
 96         for(int i=1;i<=n;i++)
 97         {
 98             char s[100+5];
 99             int t;
100             scanf("%s%d",s,&t);
101             Insert(s,t);
102         }
103 
104         scanf("%d",&n);
105         while(n--)
106         {
107             scanf("%s",str);
108             int len = strlen(str);
109             for(int i=1;i<len;i++)
110             {
111                 fre = 0;
112                 Query(0,i,root,"");
113                 if(fre) cout << ans << endl;
114                 else puts("MANUALLY");
115             }
116             puts("");
117         }
118         puts("");
119     }
120 }
View Code

  那么,字典树这个专题大概是完了,依稀记得上次百度之星有一道是字典树的没做,lyf给了一个很好的板子,下次有空了再补上。= =!