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

字典樹(Trie)

用Trie(也叫字首樹)來儲存字串集合。

每個單詞的結束位置對應一個“單詞結點”。反過來,從根節點到每個單詞結點的路徑上所有字母連線而成的字串就是該結點對應的字串,在程式上,將根節點編號成0,然後把其餘結點編號為從1開始的正整數,然後用一個數組來儲存每個結點的所有子節點,用下標直接存取。

具體來說,可以用ch[i][j]儲存結點i的那個編號為j的子結點。什麼叫編號為j呢?比如,若是處理全部由小寫字母組成的字串,把所有小寫字母按照字典序編號為0,1,2,....,則ch[i][0]表示結點i的子節點a。如果這個子節點不存在,則ch[i][0]=0。用sigma_size表示字符集的大小,比如,當字符集為全體小寫字母時,sigma_size=26.

使用trie時,往往需要在單詞結點上附加資訊,其中val[i]表示結點i對應的附加資訊。例如,如果每個字串有一個權值,就可以把這個權值儲存在val[i]中。簡單起見,假定權值大於0,因此val[i]>0當且僅當結點i是單詞結點。

struct Trie{
    int ch[maxnode][sigma_size];
    int val[maxnode];
    int sz;
    void clear(){
        sz=1;
        memset(ch[0],0,sizeof(ch[0]));
        memset(val,0,sizeof(val));
    }
    int idx(char c){
        return c-'a';
    }
    void insert(char *s,int v){
        int u=0,n=strlen(s);
        for(int i=0;i<n;i++){
            int c=idx(s[i]);
            if(!ch[u][c]){
                memset(ch[sz],0,sizeof(ch[sz]));
                val[sz]=0;
                ch[u][c]=sz++;
            }
            u=ch[u][c];
        }
        val[u]=v;
    }
    void find_prefixes(char *s,int len,vector<int>&ans){
        int u=0;
        for(int i=0;i<len;i++){
            if(s[i]=='\0'){
                break;
            }
            int c=idx(s[i]);
            if(!ch[u][c]){
                break;
            }
            u=ch[u][c];
            if(val[u]!=0){
                ans.push_back(val[u]);
            }
        }
    }
};
Trie trie;