Competitive programming 2
2021/10/04
字典樹 ( or 字首樹 )
每個節點有往下的指標
以及紀錄多少個字串以此為結尾
struct trie{
trie *nxt[26];
int cnt; //紀錄有多少個字串以此節點結尾
int sz; //有多少字串的前綴包括此節點
trie():cnt(0),sz(0){
memset(nxt,0,sizeof(nxt));
}
};
//創建新的字典樹
trie *root = new trie();
將字串 \(s\) 加入字典樹
// O(|s|)
void insert(string& s){
trie *now = root; // 每次從根結點出發
for(auto i:s){
now->sz++;
if(now->nxt[i-'a'] == NULL){
now->nxt[i-'a'] = new trie();
}
now = now->nxt[i-'a']; //走到下一個字母
}
now->cnt++;
now->sz++;
}
// O(|s|)
int query_prefix(string& s){ //查詢有多少前綴為 s
trie *now = root; // 每次從根結點出發
for(auto i:s){
if(now->nxt[i-'a'] == NULL){
return 0;
}
now = now->nxt[i-'a'];
}
return now->sz;
}
int query_count(string& s){ //查詢字串 s 出現次數
trie *now = root; // 每次從根結點出發
for(auto i:s){
if(now->nxt[i-'a'] == NULL){
return 0;
}
now = now->nxt[i-'a'];
}
return now->cnt;
}
01 trie
本質也是 trie,用在跟 xor 有關的題目
看到 xor 的難題,有約 50% 是用 trie 解
struct trie{
trie *nxt[2]; // 差別
int cnt; //紀錄有多少個數字以此節點結尾
int sz; //有多少數字的前綴包括此節點
trie():cnt(0),sz(0){
memset(nxt,0,sizeof(nxt));
}
};
//創建新的字典樹
trie *root = new trie();
將數字 \(x\) 加入字典樹,O(lg x)
void insert(int x){
trie *now = root; // 每次從根結點出發
for(int i=30;i>=0;i--)
now->sz++;
if(now->nxt[x>>i&1] == NULL){
now->nxt[x>>i&1] = new trie();
}
now = now->nxt[x>>i&1]; //走到下一個字母
}
now->cnt++;
now->sz++;
}
一開始給 \(N (N \le 10^5)\) 個數字
\(Q (Q \le 10^5)\) 筆詢問,每次給你兩個數字 \(x, d\)
有多少個數字 xor \(x\) 後 \(\le d\)
詢問多少個數字 xor \(4\) 之後小於等於 \(5\)
中文叫雜湊
雜湊函式(hash function) 把資料壓縮,使得資料量變小,將資料的格式固定下來。該函式將資料打亂混合,重新建立一個叫做雜湊值。雜湊值通常用一個短的隨機字母和數字組成的字串來代表
\(H_{0,k}\) 為字串 \(s[0,k]\) hash 出來的結果
其中 \(p\) 為一個常數(通常選質數降低碰撞機率)
mod 為一個模數(通常選質數降低碰撞機率,通常選約為 \(10^9\) 附近的數字)
\(H_{0,k} = (s_0 * p^k + s_1 * p^{k-1} + s_2 * p^{k-2} ... + s_k * p^0)\) % mod
\(\to H_{0,0} = (s_0 * p^0)\) % mod
\(\to H_{0,1} = (H_{0,0} * p + s_1)\) % mod
\(\to H_{0,2} = (H_{0,1} * p + s_2)\) % mod
\(\to H_{0,i} = (H_{0,i-1} * p + s_i)\) % mod
const ll P = 75577;
const ll MOD = 998244353;
ll Hash[MXN]; //Hash[i] 為字串 [0,i] 的 hash值
void build(const string& s){
int val = 0;
for(int i=0; i<s.size(); i++){
val = (val * P + s[i]) % MOD;
Hash[i] = val;
}
}
\(Q\) 筆詢問,每次問字串\(s\)中有沒有某個子字串為\(x\)
假設要求字串 \(s\) 區間[l,r]的Hash值為
\(H_{l,r} = (s_l * p^{r-l+1} + s_{l+1} * p^{r-l} + s_{l+2} * p^1 + ... +s_r * p^0)\) % mod
\(\to (H_r - H_{l-1} * p^{r-l+1})\)
以區間[1,3]為例
\(H_{0,3} = (s_0 * p^3 + s_1 * p^2 + s_2 * p^1 + s_3 * p^0)\) % mod
\(H_{0,0} = (s_0 * p^0)\) % mod
\(H_{0,3} - H_{0,0} * p^3\)
\(=(s_0 * p^3 + s_1 * p^2 + s_2 * p^1 + s_3 * p^0)-(s_0 * p^3)\) % mod
\(=(s_1 * p^2 + s_2 * p^1 + s_3 * p^0)\) % mod
\(=H_{1,3}\)
因此我們可以 \(O(N)\) 時間建 Hash 表
\(O(1)\) 時間計算所有子字串的 Hash 值
但是還是有可能發生碰撞
在這種情況下,我們會再多選一個 \(p_2\) 或者 \(mod_2\)
使得每個區間都有兩個Hash值
const ll P1 = 75577;
const ll P2 = 12721; // 多一個質數 p2
const ll MOD = 998244353;
pair<ll,ll> Hash[MXN]; //Hash[i] 為字串 [0,i] 的 hash值
void build(const string& s){
pair<ll,ll> val = make_pair(0,0);
for(int i=0; i<s.size(); i++){
val.first = (val.first * P1 + s[i]) % MOD;
val.second = (val.second * P2 + s[i]) % MOD;
Hash[i] = val;
}
}
提交期限兩星期,下下星期一 18:30 截止
or
or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up
Syntax | Example | Reference | |
---|---|---|---|
# Header | Header | 基本排版 | |
- Unordered List |
|
||
1. Ordered List |
|
||
- [ ] Todo List |
|
||
> Blockquote | Blockquote |
||
**Bold font** | Bold font | ||
*Italics font* | Italics font | ||
~~Strikethrough~~ | |||
19^th^ | 19th | ||
H~2~O | H2O | ||
++Inserted text++ | Inserted text | ||
==Marked text== | Marked text | ||
[link text](https:// "title") | Link | ||
 | Image | ||
`Code` | Code |
在筆記中貼入程式碼 | |
```javascript var i = 0; ``` |
|
||
:smile: | ![]() |
Emoji list | |
{%youtube youtube_id %} | Externals | ||
$L^aT_eX$ | LaTeX | ||
:::info This is a alert area. ::: |
This is a alert area. |
On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?
Please give us some advice and help us improve HackMD.
Do you want to remove this version name and description?
Syncing