• Trie树


    两个星期没有刷题了,,从今天开始吧,先从hiho开始刷,巩固一下之前学的。。

    Trie树专题。。

    题目链接:HDU - 1075

    题意:给出火星文单词对应的英文单词,输入一段火星文,输出英文。

    用trie树做,节点挂上对应的中文单词。

    也可以用map做。

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <cctype>
     4 char a[15],b[15],s[3010];
     5 
     6 struct Trie
     7 {
     8     char c[15];
     9     Trie *nex[26];
    10     int flag;
    11     Trie()
    12     {
    13         flag=0;
    14         for(int i=0;i<26;i++)
    15             nex[i]=NULL;
    16     }
    17 };
    18 Trie rt;
    19 void inser(char *b, char *a)
    20 {
    21     Trie *head=&rt;
    22     int len=strlen(b);
    23     for(int i=0;i<len;i++)
    24     {
    25         int k=b[i]-'a';
    26         if(head->nex[k]==NULL)
    27         {
    28             Trie *node=new Trie;
    29             head->nex[k]=node;
    30         }
    31         head=head->nex[k];
    32     }
    33     head->flag=1;
    34     strcpy(head->c,a);
    35 }
    36 
    37 int get(char *a)
    38 {
    39     Trie *head=&rt;
    40     int len=strlen(a);
    41     for(int i=0;i<len;i++)
    42     {
    43         int k=a[i]-'a';
    44         if(head->nex[k]==NULL) return 0;
    45         head=head->nex[k];
    46     }
    47     if(head->flag==1)
    48     {
    49         strcpy(b,head->c);
    50         return 1;
    51     }
    52     else return 0;
    53 }
    54 int main()
    55 {
    56     scanf("%s",a);
    57     while(scanf("%s",a))
    58     {
    59         if(strcmp(a,"END")==0) break;
    60         scanf("%s",b);
    61         inser(b,a);
    62     }
    63     scanf("%s",a);
    64     getchar();  //
    65     while(gets(s)) //应该是读入了换行符
    66     {
    67         if(strcmp(s,"END")==0) break;
    68         int len=strlen(s);
    69         int k=0;
    70         for(int i=0;i<len;i++)
    71         {
    72             if(islower(s[i]))
    73             {
    74                 a[k++]=s[i];
    75             }
    76             else
    77             {
    78                 a[k]=0;
    79                 if(get(a))  printf("%s",b);
    80                 else printf("%s",a);
    81                 printf("%c",s[i]);
    82                 k=0;
    83             }
    84         }
    85         puts("");
    86     }
    87     return 0;
    88 }
    trie
     1 #include <cstdio>
     2 #include <cstring>
     3 #include <iostream>
     4 #include <map>
     5 #include <string>
     6 #include <cctype>
     7 using namespace std;
     8 
     9 const int maxn=100010;
    10 map< string ,string > m;
    11 string s,a,b;
    12 char c;
    13 int main()
    14 {
    15     cin>>s;
    16     while(cin>>a)
    17     {
    18         if(a=="END") break;
    19         cin>>b;
    20         m[b]=a;
    21     }
    22     cin>>s;
    23     getchar(); //读入换行符,否则会读到下面的s里面去,又会输出到START行的末尾,发生难以察觉的错误-_-||
    24     a="";
    25     while(getline(cin,s)) //边读入边处理快很多,不用一次读完一行  (getline对读入换行符)
    26     {
    27         if(s=="END") break;
    28         int len=s.length();
    29         for(int i=0;i<len;i++)
    30         {
    31             if(islower(s[i]))  a+=s[i];
    32             else
    33             {
    34                 if(m.count(a)) cout<<m[a];
    35                 else cout<<a;
    36                 cout<<s[i];
    37                 a="";
    38             }
    39         }
    40         cout<<endl;
    41     }
    42     return 0;
    43 }
    map

    题目链接:POJ - 2001

    题意:给出一些单词,判断至少要多少位才可以唯一确定一个单词,输出对应的位数。

    trie树查找的时候,找到一位不同时就直接输出,找不到就输出原串即可。

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <cctype>
     4 const int maxn=1010;
     5 
     6 char s[maxn][25];
     7 struct Trie
     8 {
     9     Trie *nex[26];
    10     int cnt;
    11     Trie ()
    12     {
    13         cnt=0;
    14         for(int i=0;i<26;i++) nex[i]=NULL;
    15     }
    16 };
    17 Trie rt;
    18 
    19 void inser(char *a)
    20 {
    21     Trie *head=&rt;
    22     int len=strlen(a);
    23     for(int i=0;i<len;i++)
    24     {
    25         int k=a[i]-'a';
    26         if(head->nex[k]==NULL)
    27         {
    28             Trie *node =new Trie;
    29             head->nex[k]=node;
    30         }
    31         head=head->nex[k];
    32         head->cnt++;
    33     }
    34 }
    35 
    36 void get(char *a)
    37 {
    38     Trie *head=&rt;
    39     int len=strlen(a);
    40     for(int i=0;i<len;i++)
    41     {
    42         int k=a[i]-'a';
    43         if(head->nex[k]==NULL)
    44         {
    45             printf("%s
    ",a);
    46             return;
    47         }
    48         if(head->cnt==1)
    49         {
    50             a[i]=0;
    51             printf("%s
    ",a);
    52             return ;
    53         }
    54         head=head->nex[k];
    55     }
    56     printf("%s
    ",a);
    57     return;
    58 }
    59 
    60 int main()
    61 {
    62     int n=0;
    63     while(scanf("%s",s[n++])!=EOF)
    64     {
    65         inser(s[n-1]);
    66     }
    67     for(int i=0;i<n;i++)
    68     {
    69         printf("%s ",s[i]);
    70         get(s[i]);
    71     }
    72 }
    trie

    以上几题是很久之前做的了,补了一点题解,代码风格可能不太统一。



    Remember the Word

     UVALive - 3942

    题意:输入一些单词和一个长字符串,问有多少种分割该字符串的方案(只能分割成给出的单词)。

    学习了lrj不用指针的trie树,感觉还不错。也是很久之前做的了,重新整理一下。

    令d[i]表示从i到末尾的字符串s[i……L]可以分割的方案数

    从后往前不断更新,d[i]=sum(d[i+len(x)],其中x是s[i……L]的前缀。

     1 #include<cstdio>
     2 #include<cstring>
     3 const int maxnode=500010;
     4 const int sigma=26;
     5 const int N=300010;
     6 const int mod=20071027;
     7 int ch[maxnode][sigma];
     8 int val[maxnode];
     9 int sz;
    10 inline int id(char c) {return c-'a';}
    11 
    12 void trie_init()
    13 {
    14     sz=1;
    15     memset(ch[0],0,sizeof(ch[0]));
    16 }
    17 
    18 void inser(char *s,int v)
    19 {
    20     int u=0;
    21     for(int i=0;s[i];i++)
    22     {
    23         int c=id(s[i]);
    24         if(!ch[u][c])
    25         {
    26             memset(ch[sz],0,sizeof(ch[sz]));
    27             val[sz]=0;
    28             ch[u][c]=sz++;
    29         }
    30         u=ch[u][c];
    31     }
    32     val[u]=v;
    33 }
    34 int num,dp[N],st;
    35 void query(char *s)
    36 {
    37     int u=0;
    38     for(int i=0;s[i];i++)
    39     {
    40         int c=id(s[i]);
    41         if(ch[u][c])
    42         {
    43             u=ch[u][c];
    44             if(val[u]) num=(num+dp[st+i+1])%mod;
    45         }
    46         else return;
    47     }
    48 }
    49 int main()
    50 {
    51     char s[N],p[110];
    52     int k=0;
    53     while(scanf("%s",s)!=EOF)
    54     {
    55         trie_init();
    56         int m;
    57         scanf("%d",&m);
    58         while(m--)
    59         {
    60             scanf("%s",p);
    61             inser(p,1);
    62         }
    63         int len=strlen(s);
    64         dp[len]=1;
    65         for(int i=len-1;i>=0;i--)
    66         {
    67             num=0;
    68             st=i;
    69             query(&s[i]);
    70             dp[i]=num;
    71         }
    72         printf("Case %d: %d
    ",++k,dp[0]);
    73     }
    74     return 0;
    75 }
    View Code

    "strcmp()" Anyone?

     UVA - 11732

    题意:给n个串,问至少需要比较多少次?(每比较一个字符(如果相等' '也要比一次)算比较一次,任意两个串都要进行比较大小)

    首先数据较大,如果用之前的方法建Trie树,会爆内存,因为最多可能有4000×1000×26个点,一个亿多了=_=

    lrj说用左二子右兄弟表示法保存Trie树,看起来感觉很像链式前向星,只需要4000×1000个点,四百万个。总之学习了▄█▀█●

    存完图还要计算比较多少次啊,看了很久才理解,太菜了。。。

        void dfs(int dep,int u)
        {
            if(head[u]==-1) ans+=tot[u]*(tot[u]-1)*dep; //注释1
            else {
                int sum=0;
                for(int v=head[u];~v;v=nex[v]){
                    sum+=tot[v]*(tot[u]-tot[v]);  //注释2
                }
                ans+=sum/2*(2*dep+1); // 注释3
                for(int v=head[u];~v;v=nex[v])
                    dfs(dep+1,v);
            }
        }

    注释1:如果这些个串要比较到' ',说明这些串完全相同,每两个串的每一位都要比较一次(按题目说的是2次)

    注释2:对于中间的边(字母),每次考虑它的不同子树,同一个子树内的串先不比较。子树v中选一个串,其他子树中再选一个。求和。

    注释3:  2那一步中计算重复了,所以除以2,再乘以(dep×2+1)是什么意思呢?加1就是计算当前这一层了,而dep*2是计算之前的字母,因为之前的字母相同所以每次比较次数为2.

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 #define LL long long
     4 #define CLR(m,a) memset(m,a,sizeof(m))
     5 
     6 const int maxnode=4000*1000+10;
     7 const int sigma=26;
     8 
     9 struct Trie
    10 {
    11     int head[maxnode],nex[maxnode],tot[maxnode];
    12     char ch[maxnode];
    13     int sz;
    14     LL ans; //答案
    15 
    16     void init(){sz=1; tot[0]=0;head[0]=nex[0]=-1;}
    17     void inser(char *s)
    18     {
    19         int u=0,v,n=strlen(s);
    20         tot[0]++;
    21         for(int i=0;i<=n;i++){
    22             int ok=0;
    23             for(v=head[u];~v;v=nex[v]) if(ch[v]==s[i]){
    24                 ok=1;
    25                 break;
    26             }
    27             if(!ok){
    28                 v=sz++;  //新建节点
    29                 tot[v]=0;
    30                 ch[v]=s[i];
    31                 nex[v]=head[u];
    32                 head[u]=v;
    33                 head[v]=-1;
    34             }
    35             u=v;
    36             tot[u]++;
    37         }
    38     }
    39     void dfs(int dep,int u)
    40     {
    41         if(head[u]==-1) ans+=tot[u]*(tot[u]-1)*dep;
    42         else {
    43             int sum=0;
    44             for(int v=head[u];~v;v=nex[v]){
    45                 sum+=tot[v]*(tot[u]-tot[v]);  //子树v中选一个串,其他子树中再选一个
    46             }
    47             ans+=sum/2*(2*dep+1); // 除以2是每种选法统计了两次
    48             for(int v=head[u];~v;v=nex[v])
    49                 dfs(dep+1,v);
    50         }
    51     }
    52 
    53     LL solve()
    54     {
    55         ans=0;
    56         dfs(0,0);
    57         return ans;
    58     }
    59 };
    60 Trie trie;
    61 const int maxn=1010;
    62 int n;
    63 char s[maxn];
    64 
    65 int main()
    66 {
    67     int kase=0;
    68     while(scanf("%d",&n)!=EOF&&n){
    69         trie.init();
    70         for(int i=0;i<n;i++){
    71             scanf("%s",s);
    72             trie.inser(s);
    73         }
    74         printf("Case %d: %lld
    ",++kase,trie.solve());
    75     }
    76     return 0;
    77 }
    View Code

    Hyper Prefix Sets

     UVA - 11488

    题意:找所有串的最长公公前缀,再乘以串数。

    第一次写的时候,是先插入,最后查找的时候统计结果,看了别人的代码,发现可以变插入边统计。

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int maxnode=50000*200+10;
     4 const int sigma=2;
     5 #define CLR(m,a) memset(m,a,sizeof(m))
     6 struct Trie{
     7     int ch[maxnode][sigma];
     8     int cnt[maxnode];
     9     int sz;
    10     int ans;
    11 
    12     void init(){
    13         CLR(ch[0],0);
    14         CLR(cnt,0);
    15         sz=1;
    16         ans=0;
    17     }
    18     int idx(char c){return c-'0';}
    19     void inser(char *s){
    20         int u=0,n=strlen(s);
    21         for(int i=0;i<n;i++){
    22             int c=idx(s[i]);
    23             if(!ch[u][c]){
    24                 CLR(ch[sz],0);
    25                 ch[u][c]=sz++;
    26             }
    27             u=ch[u][c];
    28             cnt[u]++;
    29         }
    30 
    31     }
    32     void find(int u,int d){
    33         for(int c=0;c<sigma;c++){
    34             if(!ch[u][c]) continue;
    35             ans=max(ans,cnt[ch[u][c]]*d);
    36             find(ch[u][c],d+1);
    37         }
    38     }
    39 };
    40 Trie trie;
    41 char s[210];
    42 int main()
    43 {
    44     int t;
    45     scanf("%d",&t);
    46     while(t--){
    47         int m;
    48         trie.init();
    49         scanf("%d",&m);
    50         while(m--){
    51             scanf("%s",s);
    52             trie.inser(s);
    53         }
    54         trie.find(0,1);
    55         printf("%d
    ",trie.ans);
    56     }
    57 }
    最后统计
     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int maxnode=50000*200+10;
     4 const int sigma=2;
     5 #define CLR(m,a) memset(m,a,sizeof(m))
     6 struct Trie{
     7     int ch[maxnode][sigma];
     8     int cnt[maxnode];
     9     int sz;
    10     int ans;
    11 
    12     void init(){
    13         CLR(ch[0],0);
    14         CLR(cnt,0);
    15         sz=1;
    16         ans=0;
    17     }
    18     int idx(char c){return c-'0';}
    19     void inser(char *s){
    20         int u=0,n=strlen(s);
    21         for(int i=0;i<n;i++){
    22             int c=idx(s[i]);
    23             if(!ch[u][c]){
    24                 CLR(ch[sz],0);
    25                 ch[u][c]=sz++;
    26             }
    27             u=ch[u][c];
    28             cnt[u]++;
    29             ans=max(ans,cnt[u]*(i+1));
    30         }
    31     }
    32 
    33 };
    34 Trie trie;
    35 char s[210];
    36 int main()
    37 {
    38     int t;
    39     scanf("%d",&t);
    40     while(t--){
    41         int m;
    42         trie.init();
    43         scanf("%d",&m);
    44         while(m--){
    45             scanf("%s",s);
    46             trie.inser(s);
    47         }
    48         printf("%d
    ",trie.ans);
    49     }
    50 }
    同时统计结果
  • 相关阅读:
    smarty对网页性能的影响--开启opcache
    1stopt8.0 代码示例
    1stopt、matlab和python用morris、sobol方法实现参数敏感性分析
    MATLAB 实现sobol参数敏感性分析
    matlab中自带的sobol的函数提供的sobol序列
    matlab和fortran混合编程
    mathematic语法基础
    fortran常用语句--读写带注释文档、动态数组等语法
    fortran语言调用fortran写的dll
    C语言函数指针与 c#委托和事件对比
  • 原文地址:https://www.cnblogs.com/yijiull/p/6892087.html
Copyright © 2020-2023  润新知