Powered by:NEFU AB_IN
Trie樹
模闆帶注釋
// son[][]存儲樹中每個節點的子節點
// cnt[]存儲以每個節點結尾的單詞數量
namespace Trie{
const int N = 1e5 + 10;
int son[N][26], cnt[N], idx; // 'son[N][10]'
void init(){
memset(son, 0, sizeof son);
memset(cnt, 0, sizeof cnt);
idx = 0;
}
void insert(char *s){
int p = 0; // p代表每個節點的下标,現在為根節點
for(int i = 0; s[i]; ++ i){
int u = s[i] - 'a'; // '0'代表減數字時
// u代表p結點的子節點,但不是下标,u和p性質不一樣
if(!son[p][u]) son[p][u] = ++ idx; // 建立節點,每個節點都有自己的下标
p = son[p][u]; // 往下走
}
cnt[p] ++; // 統計以p下标結尾單詞的數量
}
int query(char *s){
int p = 0;
for(int i = 0; s[i]; ++ i){
int u = s[i] - 'a'; // '0'
if(!son[p][u]) return 0;
p = son[p][u];
}
return cnt[p];
}
}
using namespace Trie;
無注釋
namespace Trie{
const int N = 1e5 + 10;
int son[N][26], cnt[N], idx;
void init(){
memset(son, 0, sizeof son);
memset(cnt, 0, sizeof cnt);
idx = 0;
}
void insert(char *s){
int p = 0;
for(int i = 0; s[i]; ++ i){
int u = s[i] - 'a';
if(!son[p][u]) son[p][u] = ++ idx;
p = son[p][u];
}
cnt[p] ++;
}
int query(char *s){
int p = 0;
for(int i = 0; s[i]; ++ i){
int u = s[i] - 'a';
if(!son[p][u]) return 0;
p = son[p][u];
}
return cnt[p];
}
}
using namespace Trie;
Phone List
-
題意
給定 n n n個長度不超過 10 10 10的數字串,問其中是否存在兩個數字串 S , T S,T S,T,使得 S S S是 T T T的字首,多組資料。 -
思路
T r i e Trie Trie樹模闆題
正常思路就是把每個字元串都進樹,然後離線處理,這樣時間和空間複雜度都比較大
是以我們可以做到動态查詢
- 比如 91 91 91和 9134 9134 9134, 91 91 91以 1 1 1結尾,那麼當 9134 9134 9134周遊到 1 1 1時,發現這個下标的 c n t ≠ 0 cnt \ne0 cnt=0,說明前面有單詞 91 91 91,那麼就不符合
- 比如 9134 9134 9134和 91 91 91, 91 91 91周遊到 1 1 1的時候,發現這個下标的 c n t = 0 cnt = 0 cnt=0,但是 91 91 91是 9134 9134 9134的字首,也不符合要求。要做到這點,隻需要一開始記個 f l a g = f a l s e flag = false flag=false,如果插入的過程中開點了,那麼 f l a g = t u r e flag = ture flag=ture,像剛才就沒有動态開點,是以依然是 f a l s e false false
/* * @Author: NEFU AB_IN * @Date: 2021-08-26 14:00:53 * @FilePath: \Vscode\ACM\Project\Trie\Poj3630.cpp * @LastEditTime: 2021-08-26 15:25:14 */ #include<iostream> #include<vector> #include<cstring> #include<string.h> #include<cstdio> #include<climits> #include<cmath> #include<algorithm> #include<queue> #include<deque> #include<map> #include<set> #include<string> #include<stack> using namespace std; #define LL long long #define MP make_pair #define SZ(X) ((int)(X).size()) #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define DEBUG(X) cout << #X << ": " << X << endl; typedef pair<int , int> PII; namespace Trie{ const int N = 1e5 + 10; int son[N][10], cnt[N], idx; void init(){ memset(son, 0, sizeof son); memset(cnt, 0, sizeof cnt); idx = 0; } bool insert(char *s){ int p = 0, flag = 0; for(int i = 0; s[i]; ++ i){ int u = s[i] - '0'; if(!son[p][u]) son[p][u] = ++ idx, flag = 1; p = son[p][u]; if(cnt[p]) return false; } cnt[p] ++; return flag; } int query(char *s){ int p = 0; for(int i = 0; s[i]; ++ i){ int u = s[i] - '0'; if(!son[p][u]) return 0; p = son[p][u]; } return cnt[p]; } } using namespace Trie; int t; char s[N]; signed main() { IOS cin >> t; while(t --){ init(); int n, flag = 0; cin >> n; for(int i = 1; i <= n; ++ i){ cin >> s; if(!insert(s)) flag = 1; } if(flag) cout << "NO\n"; else cout << "YES\n"; } return 0; }
01Trie樹
模闆帶注釋
01 01 01字典樹,每個分支隻有 01 01 01兩個,主要解決異或問題
如果多組資料運用 01 t r i e 01trie 01trie樹時,需要進行初始化,可以像正常 t r i e trie trie樹一樣進行 m e m s e t memset memset操作,但有些題目就會 T T T,這時我們可以采用更好的方法進行初始化。觀察到分支隻有兩個,那麼就可以在建立新分支的同時,對新分支的兩個 01 01 01子節點進行初始化,注意别忘了一開始也要對根節點的子節點進行初始化!
namespace Trie01{
const int N = 1e5 + 10;
int son[N * 32][2], val[N * 32], idx; // n個數,每個數最多被拆成不超過32位
void init(){
idx = 0;
son[0][0] = son[0][1] = 0;
// val沒必要進行初始化,因為是可以覆寫的
}
void insert(int x){
int p = 0;
for(int i = 31; i >= 0; -- i){
// 貪心時建構01trie樹的基礎,從二進制的高位開始貪心
int u = (x >> i) & 1;
if(!son[p][u]){
son[p][u] = ++ idx;
p = son[p][u];
// 構造新節點的同時初始化新節點
son[p][0] = son[p][1] = 0;
}
else p = son[p][u];
}
val[p] = x; // 記錄終點
}
int query(int x){
int p = 0;
for(int i = 31; i >= 0; -- i){
int u = (x >> i) & 1;
// 貪心的選擇優先走和目前位不同的路
if(son[p][u ^ 1]) p = son[p][u ^ 1];
else p = son[p][u];
}
return val[p];
}
}
using namespace Trie01;
無注釋
namespace Trie01{
const int N = 1e5 + 10;
int son[N * 32][2], val[N * 32], idx;
void init(){
idx = 0;
son[0][0] = son[0][1] = 0;
}
void insert(int x){
int p = 0;
for(int i = 31; i >= 0; -- i){
int u = (x >> i) & 1;
if(!son[p][u]){
son[p][u] = ++ idx;
p = son[p][u];
son[p][0] = son[p][1] = 0;
}
else p = son[p][u];
}
val[p] = x;
}
int query(int x){
int p = 0;
for(int i = 31; i >= 0; -- i){
int u = (x >> i) & 1;
if(son[p][u ^ 1]) p = son[p][u ^ 1];
else p = son[p][u];
}
return val[p];
}
}
using namespace Trie01;
Xor Sum(hdu4825)
-
題意
n n n個正整數, m m m次詢問,找一個正整數 K K K,使得 K K K 與 S S S 的異或結果最大 -
思路
01 T r i e 01Trie 01Trie闆子題/* * @Author: NEFU AB_IN * @Date: 2021-08-26 16:40:07 * @FilePath: \Vscode\ACM\Project\Trie\hdu4825.cpp * @LastEditTime: 2021-08-26 22:54:39 */ #include<bits/stdc++.h> using namespace std; #define LL long long #define MP make_pair #define SZ(X) ((int)(X).size()) #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define DEBUG(X) cout << #X << ": " << X << endl; typedef pair<int , int> PII; namespace Trie01{ const int N = 1e5 + 10; int son[N * 32][2], val[N * 32], idx; void init(){ idx = 0; son[0][0] = son[0][1] = 0; } void insert(int x){ int p = 0; for(int i = 31; i >= 0; -- i){ int u = (x >> i) & 1; if(!son[p][u]){ son[p][u] = ++ idx; p = son[p][u]; son[p][0] = son[p][1] = 0; } else p = son[p][u]; } val[p] = x; } int query(int x){ int p = 0; for(int i = 31; i >= 0; -- i){ int u = (x >> i) & 1; if(son[p][u ^ 1]) p = son[p][u ^ 1]; else p = son[p][u]; } return val[p]; } } using namespace Trie01; int t, n, m, x; signed main() { IOS while(~scanf("%d", &t)){ for(int j = 1; j <= t; ++ j){ init(); scanf("%d%d", &n, &m); for(int i = 1; i <= n; ++ i){ scanf("%d", &x); insert(x); } printf("Case #%d:\n", j); for(int i = 1; i <= m; ++ i){ scanf("%d", &x); printf("%d\n", query(x)); } } } return 0; }
A - L 語言
-
題意
給出 n n n個字元串構成一個字典 D D D, m m m次詢問,每次詢問一個字元串,輸出這段字元串在字典 D D D可以被了解的最長字首的位置 -
思路
dp + trie
思路都在注釋中
這裡的查詢函數改了,改成了從字元串的某處開始查詢
主要思路
- 先,從頭開始進行 d p dp dp,遇到了字首就标記 d p [ ] = 1 dp[] = 1 dp[]=1記作下次 d p dp dp的起點
- 後,從後往前周遊,最先 d p [ ] = 1 dp[] = 1 dp[]=1的下标就是最佳答案
/* * @Author: NEFU AB_IN * @Date: 2021-08-27 01:01:30 * @FilePath: \Vscode\ACM\Project\Trie\L.cpp * @LastEditTime: 2021-08-27 01:34:09 */ #include<bits/stdc++.h> using namespace std; #define LL long long #define MP make_pair #define SZ(X) ((int)(X).size()) #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define DEBUG(X) cout << #X << ": " << X << endl; typedef pair<int , int> PII; namespace Trie{ const int N = 2e6 + 20; int n, m, dp[N]; int son[N][26], cnt[N], idx; char s[N]; void init(){ memset(son, 0, sizeof son); memset(cnt, 0, sizeof cnt); idx = 0; } void insert(char *s){ int p = 0; for(int i = 0; s[i]; ++ i){ int u = s[i] - 'a'; if(!son[p][u]) son[p][u] = ++ idx; p = son[p][u]; } cnt[p] ++; } void query(int x){ // the searching start index of string s int p = 0; for(int i = x; s[i]; ++ i){ int u = s[i] - 'a'; if(!son[p][u]) break; p = son[p][u]; if(cnt[p]) dp[i] = 1; // explain there exists a word's end, so next time // dp will call this function, the param is id plus one } } } using namespace Trie; signed main() { IOS; cin >> n >> m; for(int i = 1; i <= n; ++ i){ cin >> s; insert(s); } for(int j = 1; j <= m; ++ j){ memset(dp, 0, sizeof dp); cin >> s + 1; dp[0] = 1; // init for(int i = 0; s[i]; ++ i){ if(dp[i]) query(i + 1); } for(int i = strlen(s); i >= 0; -- i){ // searching from end to start, in order to gain the max number depend // on greedy thought, and has made sure if there r not exist a solution // then output 0 because of dp[0] = 1 if(dp[i]){ cout << i << '\n'; break; } } } return 0; }
B - Secret Message 秘密資訊
-
題意
有多少資訊和這條暗号有着相同的字首,這些資訊和暗号都是不完整的 -
思路
既然是不完整的,那麼暗号裡隻要找到在 t r i e trie trie可以比對的即可
比如 11 11 11 配對 1 , 110 1,110 1,110,就分成了兩種情況
- 一種就是像 1 1 1這種在比對路上
- 一種就是像 110 110 110正常結束的
/* * @Author: NEFU AB_IN * @Date: 2021-08-27 02:41:04 * @FilePath: \Vscode\ACM\Project\Trie\SecretMessage.cpp * @LastEditTime: 2021-08-27 09:57:06 */ #include<bits/stdc++.h> using namespace std; #define LL long long #define MP make_pair #define SZ(X) ((int)(X).size()) #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define DEBUG(X) cout << #X << ": " << X << endl; typedef pair<int , int> PII; namespace Trie{ const int N = 2e6 + 10; int son[N][2], cnt[N], idx, ans[N]; char s[N]; void init(){ memset(son, 0, sizeof son); memset(cnt, 0, sizeof cnt); idx = 0; } void insert(char *s){ int p = 0; for(int i = 0; s[i]; ++ i){ int u = s[i] - '0'; if(!son[p][u]) son[p][u] = ++ idx; p = son[p][u]; ans[p] ++; } cnt[p] ++; } int query(char *s){ int p = 0, tot = 0, flag = 0; for(int i = 0; s[i]; ++ i){ int u = s[i] - '0'; if(!son[p][u]) { flag = 1; break; } p = son[p][u]; tot += cnt[p]; } if(!flag) tot += ans[p] - cnt[p]; return tot; } } using namespace Trie; int n, m; signed main() { IOS; cin >> n >> m; for(int i = 1; i <= n; ++ i){ int k, idx = 0; cin >> k; for(int j = 1; j <= k; ++ j){ char x; cin >> x; s[idx ++] = x; } s[idx] = '\0'; insert(s); } for(int i = 1; i <= m; ++ i){ int k, idx = 0; cin >> k; for(int j = 1; j <= k; ++ j){ char x; cin >> x; s[idx ++] = x; } s[idx] = '\0'; cout << query(s) << '\n'; } return 0; }
D - The XOR Largest Pair
-
題意
在給定的 N N N 個整數 A 1 , A 2 , … , A N A1,A2,…,AN A1,A2,…,AN 中選出兩個進行異或運算,得到的結果最大是多少? -
思路
O ( n ) O(n) O(n)周遊的 01 T r i e 01Trie 01Trie/* * @Author: NEFU AB_IN * @Date: 2021-08-27 10:11:04 * @FilePath: \Vscode\ACM\Project\Trie\poj3764.cpp * @LastEditTime: 2021-08-27 10:32:58 */ #include<iostream> #include<vector> #include<cstring> #include<string.h> #include<cstdio> #include<climits> #include<cmath> #include<algorithm> #include<queue> #include<deque> #include<map> #include<set> #include<string> #include<stack> using namespace std; #define LL long long #define MP make_pair #define SZ(X) ((int)(X).size()) #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define DEBUG(X) cout << #X << ": " << X << endl; typedef pair<int , int> PII; namespace Trie01{ const int N = 1e5 + 10; int son[N * 32][2], val[N * 32], idx; void init(){ idx = 0; son[0][0] = son[0][1] = 0; } void insert(int x){ int p = 0; for(int i = 31; i >= 0; -- i){ int u = (x >> i) & 1; if(!son[p][u]){ son[p][u] = ++ idx; p = son[p][u]; son[p][0] = son[p][1] = 0; } else p = son[p][u]; } val[p] = x; } int query(int x){ int p = 0; for(int i = 31; i >= 0; -- i){ int u = (x >> i) & 1; if(son[p][u ^ 1]) p = son[p][u ^ 1]; else p = son[p][u]; } return val[p] ^ x; } } using namespace Trie01; int n, a[N]; signed main() { cin >> n; for(int i = 1; i <= n; ++ i){ cin >> a[i]; insert(a[i]); } int ans = a[1] ^ a[2]; for(int i = 1; i <= n; ++ i){ ans = max(ans, query(a[i])); } cout << ans << '\n'; return 0; }