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\n",a);
46             return;
47         }
48         if(head->cnt==1)
49         {
50             a[i]=0;
51             printf("%s\n",a);
52             return ;
53         }
54         head=head->nex[k];
55     }
56     printf("%s\n",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\n",++k,dp[0]);
73     }
74     return 0;
75 }
View Code

 

"strcmp()" Anyone?

 UVA - 11732

题意:给n个串,问至少需要比较多少次?(每比较一个字符(如果相等'\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:如果这些个串要比较到'\n',说明这些串完全相同,每两个串的每一位都要比较一次(按题目说的是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\n",++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\n",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\n",trie.ans);
49     }
50 }
同时统计结果

 

转载于:https://www.cnblogs.com/yijiull/p/6892087.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值