Trie樹專題

Trie樹專題


下面是查詢字符串的模板,可以通過做題練習來靈活修改。

1、靜態建樹  速度快,但可能會浪費內存  有的題用動態建樹會超時,靜態就不超時
struct trie
{
    int next[maxnode][size];//小寫字母size就是26,十進制就是10,二進制就是2
    bool end[maxnode];
    int sz;
    trie()
    {
        memset(next,0,sizeof(next)); //如果值是0,就表示這個節點沒有走到過
        memset(end,false,sizeof(end));
        sz = 0;
    }

    void insert(char s[])
    {
        int now = 0;
        int len = strlen(s);
        for(int i = 0; i < len; i++)
        {
            int index = s[i] - 'a';
            if ( !next[now][index] ) next[now][index] = ++sz;
            now = next[now][index];
        }
        end[now] = true; //在最後一個字母節點處標記
    }

    bool find(char s[])
    {
        int now = 0;
        int len = strlen(s);
        for(int i = 0; i < len; i++)
        {
            int index = s[i] - 'a';
            if ( !next[now][index] ) return false; // 如果這個節點沒有拓展過,也就不存在這個字符串
            now = next[now][index];
        }
        return end[now]; //返回這個節點有沒有結束標記
    }

};
2、動態建樹  速度相對慢一些,節約內存
struct trie
{
    trie* next[size];
    bool flag;
};

void init(trie* x)
{
    x->flag = false;
    for(int i = 0; i < size; i++) x->next[i] = NULL;
}

void insert(trie* x,char s[])
{
    int len = strlen(s);
    for(int i = 0; i < len; i++)
    {
        int index = s[i] - 'a';
        if ( x->next[index] == NULL )
        {
            x->next[index] = new trie;
            init(x->next[index]);
        }
        x = x->next[index];
    }
    x->flag = true;
}

bool query(trie* x,char s[])
{
    int len = strlen(s);
    for(int i = 0; i < len; i++)
    {
        int index = s[i] - 'a';
        if ( x->next[index] == NULL ) return false;
        x = x->next[index];
    }
    return x->flag;
}

下面根據一些可以用到trie樹的題目大致分一下類


一、字符串的插入、查找

這是trie樹最基礎的應用,將字符串從第一個字母開始按順序在trie樹上走一遍,在最後一個字母節點處加一個結束標記。可以去查找一個字符串,也可以解決一個字符串是否爲另一個字符串前綴的問題。


題目:HDU 1247

題解:http://blog.csdn.net/chy20142109/article/details/50704122

 

題目:POJ 2503 

題解:http://blog.csdn.net/chy20142109/article/details/50704085

 

題目:POJ 1056

題解:http://blog.csdn.net/chy20142109/article/details/50704140

 

題目:POJ 3630

題解:http://blog.csdn.net/chy20142109/article/details/50704174



二、統計每個節點的訪問次數

在每一個節點處加一個變量來計數,表示之前加入字符串的時候這個節點到達了多少次,也就是有多少個字符串的前綴是這個字符串。


題目:HDU 1251

題解:http://blog.csdn.net/chy20142109/article/details/50704050

 

題目:POJ 2001

題解:http://blog.csdn.net/chy20142109/article/details/50704221



三、解決一些帶有異或計算的問題


我們可以把整數表示成二進制,把二進制狀態的下值插入到字典樹中進行操作。

比如:我們想在一個整數的集合中找出一個整數x,使得x和給出的一個數異或結果最大。我們考慮異或運算是按二進制位運算,相異爲1,那麼我們可以兩個數的二進制高位儘可能的相異,就能保證答案是最大的。建trie樹的時候從最高位開始往低位走,優先考慮高位,因爲高位的權值大,影響大。

 

還有一些關於異或的小技巧,因爲x^x = 0,所以當出現重複的數就會抵消。可以把這個思想運用到一些數據的處理上,現在考慮在一個區間[l,r]內,找出一段連續的數,使其異或後的結果最大/最小。我們用f[i]表示a[1]^a[2]^...^a[i],那麼如果想求一段區間[x,y]的異或值就可以用f[x-1]^f[y]來表示。

回到問題,我們枚舉這一段連續的數的結尾,也就是現在有一個f[i],i屬於[l,r],現在想找到一個f[k],k屬於[l-1,i-1],使得f[i] xor f[k]結果最大/最小。那麼找f[k]的這個過程就可以用f[i]在trie樹中去貪心匹配,然後對於每次枚舉區間結尾計算出來的最大值更新答案。


題目:HDU 4825

題解:http://blog.csdn.net/chy20142109/article/details/50704023

 

題目:HDU 5269   

題解:http://blog.csdn.net/chy20142109/article/details/50704324

 

題目:POJ 3764   

題解:http://blog.csdn.net/chy20142109/article/details/50704350


題目:CodeForces 282E  

題解:http://blog.csdn.net/chy20142109/article/details/50706248





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章