1. 程式人生 > >Trie 字典樹

Trie 字典樹

src spa main ets strcmp() ems next arc ear

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

Trie 字典樹