字典樹

一、定義

字典樹(Trie)可以保存一些字符串->值的對應關係。基本上,它跟 Java 的 HashMap 功能相同,都是 key-value 映射,只不過 Trie 的 key 只能是字符串。
  Trie 的強大之處就在於它的時間複雜度。它的插入和查詢時間複雜度都爲 O(k) ,其中 k 爲 key 的長度,與 Trie 中保存了多少個元素無關。Hash 表號稱是 O(1) 的,但在計算 hash 的時候就肯定會是 O(k) ,而且還有碰撞之類的問題;Trie 的缺點是空間消耗很高
  至於Trie樹的實現,可以用數組,也可以用指針動態分配。
Trie樹,又稱單詞查找樹或鍵樹,是一種樹形結構,是一種哈希樹的變種。典型應用是用於統計和排序大量的字符串(但不僅限於字符串),所以經常被搜索引擎系統用於文本詞頻統計。它的優點是:最大限度地減少無謂的字符串比較,查詢效率比哈希表高。
Trie的核心思想是空間換時間。利用字符串的公共前綴來降低查詢時間的開銷以達到提高效率的目的。
Trie樹有一些特性:
1)根節點不包含字符,除根節點外每一個節點都只包含一個字符。
2)從根節點到某一節點,路徑上經過的字符連接起來,爲該節點對應的字符串。
3)每個節點的所有子節點包含的字符都不相同。
4)如果字符的種數爲n,則每個結點的出度爲n,這也是空間換時間的體現,浪費了很多的空間。
5)插入查找的複雜度爲O(n),n爲字符串長度。
基本思想(以字母樹爲例):
1、插入過程
對於一個單詞,從根開始,沿着單詞的各個字母所對應的樹中的節點分支向下走,直到單詞遍歷完,將最後的節點標記爲紅色,表示該單詞已插入Trie樹。
2、查詢過程
同樣的,從根開始按照單詞的字母順序向下遍歷trie樹,一旦發現某個節點標記不存在或者單詞遍歷完成而最後的節點未標記爲紅色,則表示該單詞不存在,若最後的節點標記爲紅色,表示該單詞存在。
二、字典樹的數據結構:
typedef struct Tire_node
{
int cnt; //記錄當前前綴出現了幾次
struct Tire_node* next[26]; //視情況而定
} Tirenode,*Tire;

其中next是一個指針數組,存放着指向各個孩子結點的指針。
如給出字符串"abc",“ab”,“bd”,“dda”,根據該字符串序列構建一棵Trie樹。則構建的樹如下:
在這裏插入圖片描述

Trie樹的根結點不包含任何信息,第一個字符串爲"abc",第一個字母爲’a’,因此根結點中數組next下標爲’a’-97的值不爲NULL,其他同理,構建的Trie樹如圖所示,紅色結點表示在該處可以構成一個單詞。很顯然,如果要查找單詞"abc"是否存在,查找長度則爲O(len),len爲要查找的字符串的長度。而若採用一般的逐個匹配查找,則查找長度爲O(len*n),n爲字符串的個數。顯然基於Trie樹的查找效率要高很多。
如上圖中:Trie樹中存在的就是abc、ab、bd、dda四個單詞。在實際的問題中可以將標記顏色的標誌位改爲數量count等其他符合題目要求的變量。
已知n個由小寫字母構成的平均長度爲10的單詞,判斷其中是否存在某個串爲另一個串的前綴子串。下面對比3種方法:

1、 最容易想到的:即從字符串集中從頭往後搜,看每個字符串是否爲字符串集中某個字符串的前綴,複雜度爲O(n^2)。

2、 使用hash:我們用hash存下所有字符串的所有的前綴子串。建立存有子串hash的複雜度爲O(nlen)。查詢的複雜度爲O(n) O(1)= O(n)。

3、 使用Trie:因爲當查詢如字符串abc是否爲某個字符串的前綴時,顯然以b、c、d…等不是以a開頭的字符串就不用查找了,這樣迅速縮小查找的範圍和提高查找的針對性。所以建立Trie的複雜度爲O(nlen),而建立+查詢在trie中是可以同時執行的,建立的過程也就可以成爲查詢的過程,hash就不能實現這個功能。所以總的複雜度爲O(nlen),實際查詢的複雜度只是O(len)。
三、Trie樹的操作
在Trie樹中主要有3個操作,插入、查找和刪除。一般情況下Trie樹中很少存在刪除單獨某個結點的情況,因此只考慮刪除整棵樹。
1、插入
假設存在字符串str,Trie樹的根結點爲root。i=0,p=root。
1)取str[i],判斷p->next[str[i]-97]是否爲空,若爲空,則建立結點temp,並將p->next[str[i]-97]指向temp,然後p指向temp;
若不爲空,則p=p->next[str[i]-97];
2)i++,繼續取str[i],循環1)中的操作,直到遇到結束符’\0’,此時將當前結點p中的 exist置爲true。
2、查找
假設要查找的字符串爲str,Trie樹的根結點爲root,i=0,p=root
1)取str[i],判斷判斷p->next[str[i]-97]是否爲空,若爲空,則返回false;若不爲空,則p=p->next[str[i]-97],繼續取字符。
2)重複1)中的操作直到遇到結束符’\0’,若當前結點p不爲空並且 exist 爲true,則返回true,否則返回false。
3、刪除
刪除可以以遞歸的形式進行刪除。

Shortest Prefixes

Description

A prefix of a string is a substring starting at the beginning of the given string. The prefixes of “carbon” are: “c”, “ca”, “car”, “carb”, “carbo”, and “carbon”. Note that the empty string is not considered a prefix in this problem, but every non-empty string is considered to be a prefix of itself. In everyday language, we tend to abbreviate words by prefixes. For example, “carbohydrate” is commonly abbreviated by “carb”. In this problem, given a set of words, you will find for each word the shortest prefix that uniquely identifies the word it represents.

In the sample input below, “carbohydrate” can be abbreviated to “carboh”, but it cannot be abbreviated to “carbo” (or anything shorter) because there are other words in the list that begin with “carbo”.

An exact match will override a prefix match. For example, the prefix “car” matches the given word “car” exactly. Therefore, it is understood without ambiguity that “car” is an abbreviation for “car” , not for “carriage” or any of the other words in the list that begins with “car”.
Input

The input contains at least two, but no more than 1000 lines. Each line contains one word consisting of 1 to 20 lower case letters.
Output

The output contains the same number of lines as the input. Each line of the output contains the word from the corresponding line of the input, followed by one blank space, and the shortest prefix that uniquely (without ambiguity) identifies this word.
Sample Input

carbohydrate
cart
carburetor
caramel
caribou
carbonic
cartilage
carbon
carriage
carton
car
carbonate
Sample Output

carbohydrate carboh
cart cart
carburetor carbu
caramel cara
caribou cari
carbonic carboni
cartilage carti
carbon carbon
carriage carr
carton carto
car car
carbonate carbona

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <algorithm>
#include <set>
#include <map>
#define PI acos(-1)
const int mod = 1001113;
const int maxx = 1e9;
typedef long long LL;
using namespace std;
typedef struct Tire_node{
    int cnt;
    struct Tire_node *next[26];
}Tirenode,*Tire;
char s[1005][25];
Tire CreatTirenode(){
    Tire node = (Tire)malloc(sizeof(Tirenode));
    node->cnt=0;
    memset(node->next,0,sizeof(node->next));
    return node;
}
void Tire_insert(Tire root ,int k)
{
      Tire node = root;
      int id;
      int len = strlen(s[k]);
      for(int i=0; i<len; i++)
      {
          id = s[k][i] - 'a';
          if(node->next[id] == NULL)
            node->next[id] = CreatTirenode();
          node = node->next[id];
          node->cnt++;
      }
      return ;
}
int Tire_search(Tire root, int k)
{
    Tire node = root;
    int id,len =strlen(s[k]);
    for(int i=0; i<len; i++)
    {
        id = s[k][i] - 'a';
        node = node -> next[id];
        if(node->cnt==1)
        {
            printf("%c",id+'a');
            return 0;
        }
        else
        {
            printf("%c",id+'a');
        }
    }
    return 0;
}
int main()
{
    Tire root = CreatTirenode();
    int cnt=0;
    while(scanf("%s",s[cnt])!=EOF)
    {
        Tire_insert(root, cnt);
        cnt++;
    }
    for(int j=0;j<=cnt;j++)
    {
        printf("%s ",s[j]);
        Tire_search(root,j);
        printf("\n");
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章