zoukankan      html  css  js  c++  java
  • ACM之路(15)—— 字典树入门练习

      刷的一套字典树的题目链接: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
    ",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
    ",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:
    ",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
    ",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
    ",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:
    ",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给了一个很好的板子,下次有空了再补上。= =!

  • 相关阅读:
    工作计划
    bzoj3626:[LNOI2014]LCA
    bzoj3631:[JLOI2014]松鼠的新家
    bzoj3573: [Hnoi2014]米特运输
    bzoj4027,[HEOI2015]兔子与樱花
    bzoj3624,[Apio2008]免费道路
    bzoj2208连通数
    tyvj1153/洛谷P1262间谍网络
    Application server libraries not found && IntelliJ IDEA && tomcat
    debian9安装java8
  • 原文地址:https://www.cnblogs.com/zzyDS/p/5644519.html
Copyright © 2011-2022 走看看