Trie(字典樹)的侃侃

Trie是什麼 ?

字典樹 : 見名知意(在樹上進行查詢)。
         跟字典相關的一定與查詢有密切的關係,
         查詢就須要必定的媒介做爲支撐,樹就爲這種查詢提供支撐。

Trie作什麼 ?

實現字符串快速檢索的多叉樹結構。
常見的字符串轉化:小寫字母或者大寫字母組成的字符串,數字組成的字符串,01編碼組成的字符串。

Trie有什麼 ?

Trie 的每一個節點都擁有若干個字符指針,就是說每一個節點有多個子節點,通俗一點就是至關於古代
的大少爺能夠有多個妻子。

Trie幹什麼 ?

一、插入(將一個字符串插入到字典樹上)
二、檢索(檢索一個字符串 S 在Trie 上是否存在)

侃了這麼多,看看這貨究竟是個啥 ?

假設有單詞 : cab , cef , da 這樣三個單詞,那麼這樣三個單詞組成的圖是什麼樣的呢 ?
看下圖 : (一般還要在末尾進行標記一下,表示到字符串的末尾)

怎麼實現這個玩意呢 ?

插入 : 
像每一個單詞拼寫同樣,單詞的開頭就至關因而咱們的根,從根節點出發,向兒子節點前進。
在向下走的過程當中,看有沒有當前這個字符的節點,若是有這個節點,就順着這個節點繼續
往下走,若是沒有這個節點,就在這個節點之下再建立一個節點。
拿一個例子來講:
好比上圖的 cab 和 cef, 先插入第一個字符串,從根節點出發,第一個字符是 c,咱們發
現沒有這個字符的節點,因此建立一個節點,將指針節點進行指向,而後一直向下移動,知道
字符串結尾。再插入第二個字符串,先檢測第一個字符是否存在,咱們發現存在,因此不用
建立,直接向下移動指針節點便可。
能夠發現:
一個節點最多能夠有26個孩子。
檢索:
只須要將字符串遍歷一遍,順着根節點下來看這條路徑上是否有不存在的值,即 0,若是
沒有到末尾就發現有 0 ,說明這個字符串不存在,反之即存在。

Code :

插入:ios

void insert(char str[]) {
    int len = strlen(str),p = 0;                        // p 做爲根節點從 0 開始
    for(int i = 0; i < len; i ++) {
        int ch = str[i] - 'a';
        if(trie[p][ch] == 0) trie[p][ch] = ++ idx;  // 建立新的節點
        p = trie[p][ch];                            // 指針移動
    }
    End[p] = true;                                      // 在末尾進行標記
    return ;
}

檢索:數組

bool query(char str[]) {
    int len = strlen(str),p = 0;
    for(int i = 0; i < len; i ++ ){
        int ch = str[i] - '0';
        if(trie[p][ch] != 0) {
            p = trie[p][ch];
        } else {
            break;                            // 及時跳出
        }
    } 
    return true;
}

Example: 前綴統計

題目連接: https://www.acwing.com/problem/content/144/
        題目描述:給定N個字符串S1,S2…SN,接下來進行M次詢問,每次詢問給定一個字符串T,求S1~SN中有多少個字符串是T的前綴。
                輸入字符串的總長度不超過106,僅包含小寫字母。
                輸入格式
                第一行輸入兩個整數N,M。
                接下來N行每行輸入一個字符串Si。
                接下來M行每行一個字符串T用以詢問。
                輸出格式
                對於每一個詢問,輸出一個整數表示答案。
                每一個答案佔一行。
                輸入樣例:
                    3 2
                    ab
                    bc
                    abc
                    abc
                    efg
                輸出樣例:
                    2
                    0

析題得說: 統計每一個字符串出現的個數便可,用一個cnt[]數組記錄每一個字符串出現的個數,而後進行檢索要處理的字符串,累加結果(模板題)編碼

AC代碼:

#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int SIZE = 1e6 + 10;

int trie[SIZE][26],cnt[SIZE];
char str[SIZE];
int n,m,idx = 0;

int main(void) {
    void insert();
    int query();
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; i ++) {
        scanf("%s",str);
        insert();
    }
    while(m --) {
        scanf("%s",str);
        printf("%d\n",query());
    }
    return 0;
} 

void insert() {
    int p = 0;
    for(int i = 0; i < strlen(str); i ++) {
        int ch = str[i] - 'a';
        if(trie[p][ch] == 0) trie[p][ch] = ++ idx;
        p = trie[p][ch];
    }
    cnt[p] ++;                            // 統計該字符串的個數
    return ;
}

int query() {
    int p = 0,res = 0;
    for(int i = 0 ; i < strlen(str); i ++) {
        int ch = str[i] - 'a';
        if(trie[p][ch] != 0) {
            p = trie[p][ch];
            res += cnt[p];           // 將以該字符結尾的數量累加,最後結果就是前綴字符串的數量
        } else {
            break;
        }
    }
    return res;
}
相關文章
相關標籤/搜索