Trie 字典树

1、UVa 1401 Remember the Word

  题意:给出n个字符串集合,问其有多少种组合方式形成目标字符串。

  思路:对n个字符串集合建立Trie树,保存每个结点的字符串的顺序编号。然后对这棵树查找目标字符串每一个后缀的前缀字符串,累加。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<iostream>
 5 #include<vector>
 6 using namespace std;
 7 const int MOD = 20071027;
 8 struct Trie
 9 {
10     int word[4001 * 100][26];
11     int ex[4001 * 100];
12     int size;
13 
14     void clear()
15     {
16         memset(word[0], 0, sizeof(word[0]));
17         size = 1;
18         ex[0] = 0;
19     }
20 
21     void insert(char *s, int v)
22     {
23         int Now = 0, curchr;
24         for (int i = 0; s[i] != '\0'; i++)
25         {
26             int c = s[i] - 'a';
27             if (!word[Now][c])
28             {
29                 memset(word[size], 0, sizeof(word[size]));
30                 ex[size] = 0;
31                 word[Now][c] = size++;
32             }
33             Now = word[Now][c];
34         }
35         ex[Now] = v;
36     }
37 
38     int search(char *s, int len, vector<int>& ans)
39     {
40         int u = 0, c;
41         for (int i = 0; s[i] != '\0'&&i<len; i++)
42         {
43             c = s[i] - 'a';
44             if (!word[u][c])return 0;
45             u = word[u][c];
46             if (ex[u])
47                 ans.push_back(ex[u]);
48         }
49     }
50 } tree;
51 
52 char s[300001];
53 char tmp[101];
54 int dp[300001];
55 int n;
56 int ll[4001];//记录每个字符串的长度
57 int main()
58 {
59     int Case = 1;
60     while (~scanf("%s",s))
61     {
62         scanf("%d", &n);
63         tree.clear();
64         memset(dp, 0, sizeof(dp));
65         int len = strlen(s);
66 
67         for (int i = 0; i<n; i++)
68         {
69             scanf("%s", tmp);
70             ll[i + 1] = strlen(tmp);
71             tree.insert(tmp, i + 1);
72         }
73         dp[len] = 1;
74         for (int i = len - 1; i >= 0; i--)
75         {//找下标为i~len-1的子字符串的前缀字符串
76             vector<int>ans;
77             tree.search(s + i, len - i, ans);
78             for (int j = 0; j<ans.size(); j++)
79                 dp[i] = (dp[i] + dp[i + ll[ans[j]]]) % MOD;
80         }
81         printf("Case %d: %d\n", Case++, dp[0]);
82     }
83     return 0;
84 }
View Code

 2、UVA 11732 strcmp() Anyone?

  题意:给n个长度不超过1000的字符串,两两字符串比较,某一位相同比较2次,不相同比较1次,问需要比较多少次。

  思路:左儿子右兄弟法表示Trie树。向右找相同字符,向左添加新字符,同一兄弟层表示在同一位的字符。

  参考:http://www.cnblogs.com/sineatos/p/3888815.html

 1 #include <cstdio>
 2 #include <cstring>
 3 #define MAXN 4001010
 4 #define ll long long
 5 using namespace std;
 6 //左儿子右兄弟表示字典树
 7 int head[MAXN];// head[i]为第i个结点的左儿子编号
 8 int next[MAXN];// next[i]为第i个结点的右兄弟编号
 9 int tot[MAXN];// tot[i]为第i个结点为根的子树包含的叶结点(即以包含其的前缀的字符串个数)总数
10 int ed[MAXN];// ed[i]为第i个结点结束的字符串的个数
11 char ch[MAXN];// ch[i]为第i个结点上的字符
12 int sz;
13 ll sum;
14 void init()
15 {// 初始时只有一个根结点
16     sz = 1; head[0] = next[0] = tot[0] = 0; sum = 0;
17 }
18 
19 void insert(char *s)
20 {
21     int u, v, n = strlen(s);
22     u = 0;
23     for (int i = 0; i<n; i++)
24     {
25         bool f = 0;
26         for (v = head[u]; v != 0; v = next[v])
27         {//找字符s[i]
28             if (ch[v] == s[i])
29             {
30                 f = 1; break;
31             }
32         }
33         if (!f)
34         {//如果没找到,新建结点
35             v = sz++;
36             tot[v] = 0;
37             ed[v] = 0;
38             ch[v] = s[i];
39             next[v] = head[u];//插入首部
40             head[u] = v;
41             head[v] = 0;
42         }
43         u = v;
44         sum += tot[v] * 2;
45         tot[v]++;
46     }
47     sum += ed[u];
48     ed[u]++;
49 }
50 int main()
51 {
52     int n;
53     char s[1002];
54     int Case = 1;
55     while(~scanf("%d", &n)&&n)
56     {
57         init();
58         for (int i = 0; i<n; i++)
59         {
60             scanf("%s", s);
61             insert(s);
62         }
63         printf("Case %d: %lld\n", Case++, sum + n*(n - 1) / 2);
64     }
65     return 0;
66 }
View Code

 3、uva 11488  Hyper Prefix Sets

  题意:求n个给出的01串的最长公共前缀的长度*n的值。

  思路:字典树,每次插入一个新的01串,累计每个结点的前缀的长度。

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 using namespace std;
 5 
 6 const int maxn = 10000010;
 7 const int Char_size = 2;
 8 struct Trie
 9 {
10     int word[maxn][Char_size];//结点
11     int val[maxn];//附加信息
12     int size;//总结点数
13     int ans;
14 
15     void Init()
16     {
17         memset(word[0], 0, sizeof(word[0]));
18         size = 1;
19         val[0] = 0;
20         ans = 0;
21     }
22     int getID(char c)
23     {
24         return c - '0';
25     }
26     void Insert(char *s)
27     {
28         int now = 0, curchr;
29         for (int i = 0; s[i] != '\0'; i++)
30         {
31             int pos =getID(s[i]);
32             if (!word[now][pos])
33             {
34                 memset(word[size], 0, sizeof(word[size]));
35                 val[size] = 0;
36                 word[now][pos] = size++;
37             }
38             now = word[now][pos];
39             val[now] += i + 1;//累加到该结点的前缀的长度
40             ans = max(ans, val[now]);
41         }
42     }
43 }tree;
44 
45 char str[maxn];
46 int main()
47 {
48     int t;
49     scanf("%d", &t);
50     while (t--)
51     {
52         tree.Init();
53         int n;
54         scanf("%d", &n);
55         while (n--)
56         {
57             scanf("%s", str);
58             tree.Insert(str);
59         }
60         printf("%d\n", tree.ans);
61     }
62     return 0;
63 }
View Code

 4、uva 1519 Dictionary Size

  题意:给出n个可能重复的单词,由这些单词组成新单词,新单词可以就是原来的单词,或者新单词的前一部分为给出单词的非空前缀,后一部分为给出单词的非空后缀,求所有不同的新单词有多少个。

  思路:字典树,建立前缀和后缀字典树,ans=前缀树的新增节点数*后缀树的新增节点数-重复情况+单字符单词情况。

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 using namespace std;
 5 
 6 const int maxn = 400100;
 7 const int Char_size = 26;
 8 struct Trie
 9 {
10     int word[maxn][Char_size];//结点
11     int Count[Char_size];
12     int size;//总结点数
13 
14     void Init()
15     {
16         memset(word[0], 0, sizeof(word[0]));
17         memset(Count, 0, sizeof(Count));
18         size = 1;
19     }
20     int getID(char c)
21     {
22         return c - 'a';
23     }
24     void Insert(char *s)
25     {
26         int now = 0, curchr;
27         for (int i = 0; s[i] != '\0'; i++)
28         {
29             int pos = getID(s[i]);
30             if (!word[now][pos])
31             {
32                 memset(word[size], 0, sizeof(word[size]));
33                 word[now][pos] = size++;
34                 if (i > 0) Count[pos]++;
35             }
36             now = word[now][pos];
37         }
38     }
39 }pretree,suftree;
40 
41 char str[maxn];
42 int vis[Char_size];//统计只有一个字符的字符串的个数
43 int main()
44 {
45     int n;
46     while (~scanf("%d", &n))
47     {
48         pretree.Init();
49         suftree.Init();
50         memset(vis, 0, sizeof(vis));
51         while (n--)
52         {
53             scanf("%s", str);
54             int len = strlen(str);
55             if (len==1) vis[str[0] - 'a']=1;
56             pretree.Insert(str);
57             reverse(str, str + len);
58             suftree.Insert(str);
59         }
60         long long ans = 1ll * (pretree.size - 1)*(suftree.size - 1);
61         //如果某个字符在pretree出现x次,在suftree出现y次,考虑该字符所组成的情况则有(1+x)*(1+y)种,然而其中只有1+x+y种是不重复的,需要去掉x*y种
62         for (int i = 0; i < Char_size; i++) ans -= 1ll * pretree.Count[i] * suftree.Count[i];
63         for (int i = 0; i < Char_size; i++) ans += vis[i];//单字符字符串另外判断
64         printf("%lld\n", ans);
65     }
66     return 0;
67 }
View Code

 5、uva 1385  Billing Tables

  题意:给出旧电话表,每个记录表示电话前缀为该区间的收费方式,注意,当一个电话号码可以被多个区间包含,以排在第一个的记录为准,当然,有些收费方式invalid。现在你要新建电话表,去掉invalid的信息,同时每个记录表示电话号码的一个前缀的收费方式。要求以字典序输出同时保证条目最少(即如果存在70~79前缀,则可合并为7).

  思路:因为记录以先出现为准,所以要倒序插入每个旧表的记录,同时标记哪些是invalid.最后从小到大(位数从小到大,每一位数字从小到大)DFS即可,一次统计(针对“即如果存在70~79前缀,则可合并为7”可以向上合并条目信息),一次输出。

下面两份代码主要是处理重复的代表收费方式的字符串的方法有别。

  1 #include<iostream>
  2 #include<map>
  3 #include<string>
  4 #include<cstring>
  5 using namespace std;
  6 map<string, int>bp;
  7 const int maxn = 110;
  8 char bplan[maxn][30];
  9 char ql[maxn][30];
 10 char qr[maxn][30];
 11 char tmp[30];
 12 int isvalid[maxn];
 13 
 14 const int maxw = 4010 * 10;
 15 const int Char_size = 10;
 16 struct Trie
 17 {
 18     int w[maxw][Char_size];
 19     int val[maxw];
 20     int size;
 21     int ans;
 22 
 23     void Init()
 24     {
 25         memset(w[0], 0, sizeof(w[0]));
 26         val[0] = 0;
 27         size = 1;
 28         ans = 0;
 29     }
 30     int GetID(char c)
 31     {
 32         return c - '0';
 33     }
 34     void Insert(char *sl, char *sr, int v)
 35     {
 36         int len = strlen(sl);
 37         int nowl = 0, nowr = 0;
 38         for (int i = 0; i < len; i++)
 39         {
 40             //对已有结点拓展其所有子节点,并向下更新节点信息
 41             for (int j = 0; j < Char_size; j++)
 42             {
 43                 if (!w[nowl][j])
 44                 {
 45                     memset(w[size], 0, sizeof(w[size]));
 46                     val[size] = 0;
 47                     w[nowl][j] = size++;
 48                 }
 49                 if (val[nowl]) val[w[nowl][j]] = val[nowl];
 50                 if (!w[nowr][j])
 51                 {
 52                     memset(w[size], 0, sizeof(w[size]));
 53                     val[size] = 0;
 54                     w[nowr][j] = size++;
 55                 }
 56                 if (val[nowr]) val[w[nowr][j]] = val[nowr];
 57             }
 58 
 59             val[nowl] = val[nowr] = 0;//已经向下后删除标记
 60 
 61                                       //更新区域内的标记
 62             if (nowl == nowr)//比如7600和7899,更新76~78(不包括76,78),边界将由下次更新
 63             {
 64                 for (int j = GetID(sl[i]) + 1; j < GetID(sr[i]); j++)
 65                 {
 66                     val[w[nowl][j]] = v;
 67                 }
 68             }
 69             else//比如7600和7899,更新760~769(不包括760)和780~789(不包括789),无需更新77X。760和789边界将下次考虑
 70             {
 71                 for (int j = GetID(sl[i]) + 1; j < Char_size; j++) val[w[nowl][j]] = v;
 72                 for (int j = 0; j < GetID(sr[i]); j++) val[w[nowr][j]] = v;
 73             }
 74 
 75             nowl = w[nowl][GetID(sl[i])];//最后结尾更新封闭区间最左侧和最右侧
 76             nowr = w[nowr][GetID(sr[i])];
 77         }
 78         val[nowl] = v;
 79         val[nowr] = v;
 80     }
 81 
 82     void DFS(int now)
 83     {
 84         if (val[now])
 85         {
 86             ans += isvalid[val[now]];
 87             return;
 88         }
 89         if (!w[now][0]) return;
 90 
 91         bool flag = true;
 92         for (int i = 0; i < Char_size; i++)
 93         {
 94             DFS(w[now][i]);
 95             if (val[w[now][i]] != val[w[now][0]]) flag = false;
 96         }
 97         //合并
 98         if (flag&&isvalid[val[w[now][0]]] && now != 0)
 99         {
100             ans -= 9;
101             val[now] = val[w[now][0]];
102         }
103     }
104 
105     char pt[15];
106     void Print(int now, int len)
107     {
108         if (val[now])
109         {
110             if (isvalid[val[now]])printf("%s %s\n", pt,bplan[val[now]]);
111             return;
112         }
113         if (!w[now][0]) return;
114         for (int i = 0; i < 10; i++)
115         {
116             pt[len] = '0' + i;
117             pt[len + 1] = '\0';
118             Print(w[now][i], len + 1);
119         }
120     }
121 }tree;
122 
123 
124 int main()
125 {
126     int Case = 1;
127     int n;
128     while (~scanf("%d", &n))
129     {
130         if (Case > 1) printf("\n");
131         tree.Init();
132         bp.clear();
133         memset(isvalid, 0, sizeof(isvalid));
134         for (int i = 1; i <= n; i++)
135         {
136             scanf("%s", ql[i]);
137             scanf("%s", tmp);//'-'
138             scanf("%s", tmp);
139             int len1 = strlen(ql[i]);
140             int len2 = strlen(tmp);
141             memset(qr[i], 0, sizeof(qr[i]));//这句要有
142             strncpy(qr[i], ql[i], len1 - len2);
143             strcat(qr[i], tmp);
144             scanf("%s", bplan[i]);
145             string s = bplan[i];
146             if (!bp.count(s))
147             {
148                 bp[s] = i;
149             }
150             if (strcmp(bplan[i], "invalid")) isvalid[i] = 1;
151         }
152         for (int i = n; i >= 1; i--)
153         {
154             string s = bplan[i];
155             tree.Insert(ql[i], qr[i], bp[s]);
156         }
157         tree.DFS(0);
158         printf("%d\n", tree.ans);
159         tree.Print(0, 0);
160         Case++;
161     }
162     return 0;
163 }
View Code
  1 #include<iostream>
  2 #include<map>
  3 #include<string>
  4 #include<cstring>
  5 using namespace std;
  6 const int maxn = 110;
  7 char bplan[maxn][30];
  8 char ql[maxn][30];
  9 char qr[maxn][30];
 10 char tmp[30];
 11 int isvalid[maxn];
 12 int num[maxn];
 13 
 14 const int maxw = 4010*10;
 15 const int Char_size = 10;
 16 struct Trie
 17 {
 18     int w[maxw][Char_size];
 19     int val[maxw];
 20     int size;
 21     int ans;
 22 
 23     void Init()
 24     {
 25         memset(w[0], 0, sizeof(w[0]));
 26         val[0] = 0;
 27         size = 1;
 28         ans = 0;
 29     }
 30     int GetID(char c)
 31     {
 32         return c - '0';
 33     }
 34     void Insert(char *sl,char *sr, int v)
 35     {
 36         int len = strlen(sl);
 37         int nowl =0, nowr = 0;
 38         for (int i = 0; i < len; i++)
 39         {
 40             //对已有结点拓展其所有子节点,并向下更新节点信息
 41             for (int j = 0; j < Char_size; j++)
 42             {
 43                 if (!w[nowl][j])
 44                 {
 45                     memset(w[size], 0, sizeof(w[size]));
 46                     val[size] = 0;
 47                     w[nowl][j] = size++;
 48                 }
 49                 if (val[nowl]) val[w[nowl][j]] = val[nowl];
 50                 if (!w[nowr][j])
 51                 {
 52                     memset(w[size], 0, sizeof(w[size]));
 53                     val[size] = 0;
 54                     w[nowr][j] = size++;
 55                 }
 56                 if (val[nowr]) val[w[nowr][j]] = val[nowr];
 57             }
 58 
 59             val[nowl] = val[nowr] = 0;//已经向下后删除标记
 60 
 61             //更新区域内的标记
 62             if (nowl == nowr)//比如7600和7899,更新76~78(不包括76,78),边界将由下次更新
 63             {
 64                 for (int j = GetID(sl[i]) + 1; j < GetID(sr[i]); j++)
 65                 {
 66                     val[w[nowl][j]] = v;
 67                 }
 68             }
 69             else//比如7600和7899,更新760~769(不包括760)和780~789(不包括789),无需更新77X。760和789边界将下次考虑
 70             {
 71                 for (int j = GetID(sl[i]) + 1; j < Char_size; j++) val[w[nowl][j]] = v;
 72                 for (int j = 0; j < GetID(sr[i]); j++) val[w[nowr][j]] = v;
 73             }
 74 
 75             nowl = w[nowl][GetID(sl[i])];
 76             nowr = w[nowr][GetID(sr[i])];
 77         }
 78         val[nowl] = v;//最后结尾更新封闭区间最左侧和最右侧
 79         val[nowr] = v;
 80     }
 81 
 82     void DFS(int now)
 83     {
 84         if (val[now])
 85         {
 86             ans += isvalid[val[now]];
 87             return;
 88         }
 89         if (!w[now][0]) return;
 90 
 91         bool flag = true;
 92         for (int i = 0; i < Char_size; i++)
 93         {
 94             DFS(w[now][i]);
 95             if (val[w[now][i]] != val[w[now][0]]) flag = false;
 96         }
 97         //合并
 98         if (flag&&isvalid[val[w[now][0]]] && now != 0)
 99         {
100             ans -= 9;
101             val[now] = val[w[now][0]];
102         }
103     }
104 
105     char pt[maxn];
106     void Print(int now, int len)
107     {
108         if (val[now])
109         {
110             if(isvalid[val[now]])printf("%s %s\n", pt,bplan[val[now]]);
111             return;
112         }
113         if (!w[now][0]) return;
114         for (int i = 0; i < 10; i++)
115         {
116             pt[len] = '0' + i;
117             pt[len + 1] = '\0';
118             Print(w[now][i], len + 1);
119         }
120     }
121 }tree;
122 
123 
124 int main()
125 {
126     int Case = 1;
127     int n;
128     while (~scanf("%d", &n))
129     {
130         if (Case > 1) printf("\n");
131         tree.Init();
132         memset(isvalid, 0, sizeof(isvalid));
133         for (int i = 1; i <= n; i++)
134         {
135             scanf("%s", ql[i]);
136             scanf("%s", tmp);//'-'
137             scanf("%s", tmp);
138             int len1 = strlen(ql[i]);
139             int len2 = strlen(tmp);
140             memset(qr[i], 0, sizeof(qr[i]));//这句要有
141             strncpy(qr[i], ql[i], len1 - len2);
142             strcat(qr[i], tmp);
143             scanf("%s", bplan[i]);
144             if (strcmp(bplan[i], "invalid")) isvalid[i] = 1;
145             num[i] = i;
146             for (int j = 1; j < i; j++)
147             {
148                 if (strcmp(bplan[j], bplan[i]) == 0)
149                 {
150                     num[i] = j;
151                     break;
152                 }
153             }
154         }
155         for (int i = n; i >= 1; i--)
156         {
157             string s = bplan[i];
158             tree.Insert(ql[i], qr[i],num[i]);
159         }
160         tree.DFS(0);
161         printf("%d\n", tree.ans);
162         tree.Print(0, 0);
163         Case++;
164     }
165     return 0;
166 }
View Code

 

posted @ 2017-09-13 00:18  萌萌的美男子  阅读(205)  评论(0编辑  收藏  举报