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
題解:http://blog.csdn.net/chy20142109/article/details/50706248