Tire树相关
tire树用来快速查找一组字符串。
算法流程
int son[N][26], cnt[N], idx; char str[N];
// 插入和删除类似,每次从根节点入场,采用计数方法,0表示没有此节点,1表示存在节点。 void insert(char* str) { int p = 0; for(int i = 0; str[i]; i ++ ) { int u = str[i] - 'a'; if(!son[p][u]) son[p][u] = ++ idx; // 没有节点,创建节点 p = son[p][u]; } cnt[p] ++ ; // 记录这个单词出现次数 } int query(char* str) { int p = 0; for(int i = 0; str[i]; i ++ ) { int u = str[i] - 'a'; if(!son[p][u]) return 0; // 如果没有节点直接return p = son[p][u]; } return cnt[p]; // 返回单词出现次数 }
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 2e5 + 10; int n; int son[N][26], cnt[N], idx; char str[N]; void insert(char* str) { int p = 0; for(int i = 0; str[i]; i ++ ) { int u = str[i] - 'a'; if(!son[p][u]) son[p][u] = ++ idx; p = son[p][u]; } cnt[p] ++ ; } int query(char* str) { int p = 0; for(int i = 0; str[i]; i ++ ) { int u = str[i] - 'a'; if(!son[p][u]) return 0; p = son[p][u]; } return cnt[p]; } int main() { cin >> n; while(n -- ) { char op; cin >> op; if(op == 'I') { cin >> str; insert(str); } else { cin >> str; cout << query(str) << endl; } } return 0; }
还有一种 01 tire常用来求最大异或和。
// 要找最大的异或,用二进制进行枚举,遇到0找1,遇到1找0
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int N = 1e5 + 10, M = 31 * N; int n; int a[N]; int son[M][2], idx; void insert(int x) { int p = 0; for(int i = 30; i >= 0; i -- ) { int u = x >> i & 1; if(!son[p][u]) son[p][u] = ++ idx; p = son[p][u]; } } int query(int x) { int p = 0, res = 0; for(int i = 30; i >= 0; i -- ) { int u = x >> i & 1; if(son[p][!u]) { p = son[p][!u]; res += 1 << i; } else p = son[p][u]; } return res; } int main() { int n; cin >> n; for(int i = 1; i <= n; i ++ ) { cin >> a[i]; insert(a[i]); } int res = 0; for(int i = 1; i <= n; i ++ ) res = max(res, query(a[i])); cout << res << endl; return 0; }
判断一个字符串是否是另一个字符串的前缀。
1、快速判断是否存在一个串是当前串的前缀 (遍历路径是否存在字符串结尾)
2、快速判断当前串是否是别的串的前缀 (如果当前串在插入过程中没有创建新节点,说明当前串是别的串的前缀)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 100010, M = 10; int n; int son[N][M], idx; int cnt[N]; string str[N]; bool insert(string str) { int p = 0; bool is_match = false, new_node = false; for(int i = 0; i < str.size(); i ++ ) { int u = str[i] - '0'; if(!son[p][u]) son[p][u] = ++ idx, new_node = true; p = son[p][u]; if(cnt[p]) is_match = true; } cnt[p] ++ ; return !is_match && new_node; } int main() { int T; cin >> T; while(T -- ) { memset(son, 0, sizeof son); idx = 0; memset(cnt, 0, sizeof cnt); cin >> n; bool res = true; for(int i = 0; i < n; i ++ ) { cin >> str[i]; if(!insert(str[i])) res = false; } if(res) puts("YES"); else puts("NO"); } return 0; }
本题是要求快速求出某个串是不是另一个串的前缀。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 2e5 + 10, M = 26; string s; int son[N][M], cnt[N], idx; bool f[N]; void insert(string& str) { int p = 0; for(int i = str.size() - 1; i >= 0; i -- ) { int u = str[i] - 'A'; if(!son[p][u]) son[p][u] = ++ idx; p = son[p][u]; } cnt[p] ++ ; } bool query(int ed) { int p = 0; for(int i = ed; i > ed - 10 && i >= 0; i -- ) { int u = s[i] - 'A'; if(!son[p][u]) return false; else { p = son[p][u]; if(cnt[p] && f[i]) return true; } } return false; } int main() { while(cin >> s, s != ".") { insert(s); } s.clear(); string line; while(cin >> line) s += line; int res = 0; f[0] = true; for(int i = 1; i <= s.size(); i ++ ) { if(query(i - 1)) { f[i] = true; res = i; } } cout << res << endl; return 0; }