1.Tire字典树
(1)基本概念
Tire树 : 高效地存储和查找字符串集合的数据结构
(2)题目示例 : Trie字符串统计
①题目介绍
②解题思路
③代码示例
#include<iostream>
using namespace std;
const int N = 100010;
int son[N][26]; //用于存储节点
int count[N]; //存储以某个字符结尾的单词的数量
int Next = 0; //记录下一个单词节点是第几个 ; 下标是0的点,既是空节点又是根节点
char str[N]; //存储字符串
void insert(char* str)
{
int p = 0;
for(int i = 0 ; str[i];++i) //str最后一位是0
{
int index = str[i]-'a'; //字符映射位置
if(!son[p][index]) son[p][index] = ++Next; //该位置为空,分配一个节点
p = son[p][index];
}
//每个结尾单词在Tire树 结构中的节点是唯一的,所以 next记录节点的唯一性,找到同一个next就能统计相同单词个数
count[p]++; //以该字符结尾的单词,统计次数
}
int query(char* str)
{
int p = 0;
for(int i = 0 ; str[i] ; ++i)
{
int index = str[i]-'a';
if(!son[p][index]) return 0; //节点路径中不存在该字符节点说明没有该字符串
p = son[p][index];
}
return count[p];
}
int main()
{
int m;
cin >> m;
while(m--)
{
char op;
cin >> op >> str;
if(op == 'I') insert(str);
else cout << query(str) << endl;
}
return 0;
}
(3)题目示例 : 最大异或对
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 100010;
const int M = N*31; //31位二进制,1位符号位没用
int a[N];
int son[M][2]; //M代表最大限度存储二进制位数
int Next;
void insert(int x)
{
int p = 0;
for(int i = 30 ; i >=0 ;--i) //从高位到低位走,最大异或对
{
int index = x >> i & 1; //第i位 是0/1
if(!son[p][index]) son[p][index] = ++Next;
p = son[p][index];
}
}
int query(int x)
{
int p = 0;
int res = 0; //计算与x 异或得到最大值的 值
for(int i = 30 ; i >= 0 ;--i)
{
int index = x >> i & 1;
if(son[p][!index]) // 找与x二进制相反的数
{
p = son[p][!index];
res = res*2 + !index;
}
else
{
p = son[p][index];
res = res*2 + index;
}
}
return res;
}
int main()
{
int n;
cin >> n;
for(int i = 0 ; i < n ;++i) cin >> a[i];
int res = 0;
for(int i= 0 ; i < n;++i)
{
insert(a[i]);
res = max(res,a[i] ^ query(a[i]));
}
cout << res << endl;
//这里不管是先全部插入再查询 还是 便插入边查询 是一样的
//某个数都要 31次查找过程
return 0;
}
2.并查集
(1)基本概念
(2)题目示例 : 合并集合
#include<iostream>
#include<vector>
using namespace std;
//找集合的root节点 + 路径压缩(所有子节点都指向该集合的根节点)
int FindRoot(vector<int>& ufs,int x)
{
if(ufs[x] != x) ufs[x] = FindRoot(ufs,ufs[x]);
return ufs[x];
}
int main()
{
int n,m;
cin >> n >> m;
vector<int> ufs(n+1,0); // 0号下标位置没用
for(int i = 1 ; i <= n ; ++i) ufs[i] = i; //初始化
while(m--)
{
char op;
int a,b;
cin >> op >> a >> b;
if(op == 'M') ufs[FindRoot(ufs,a)] = FindRoot(ufs,b);
else
{
if(FindRoot(ufs,a) == FindRoot(ufs,b)) cout << "Yes" << endl;
else cout << "No" << endl;
}
}
return 0;
}
(3)题目示例 : 连通块中点的数量
#include<iostream>
#include<vector>
using namespace std;
//查找根节点+ 路径压缩
int FindRoot(vector<int>& ufs,int x)
{
if(ufs[x] != x) ufs[x] = FindRoot(ufs,ufs[x]);
return ufs[x];
}
int main()
{
int n,m;
cin >> n >> m;
vector<int> count(n+1,1); //以某个下标为根的集合数量
vector<int> ufs(n+1);
for(int i = 1 ; i <= n ; ++i) ufs[i] = i;
while(m--)
{
char op[5];
int a,b;
cin >> op ;
if(op[0] == 'C')
{
cin >> a >> b;
int root1 = FindRoot(ufs,a);
int root2 = FindRoot(ufs,b);
if(root1 == root2) continue; //很重要,否则count重复添加
ufs[root1] = root2;
count[root2] += count[root1];
}
else if(op[1] == '1')
{
cin >> a >> b;
if(FindRoot(ufs,a) == FindRoot(ufs,b)) cout << "Yes" << endl;
else cout << "No" << endl;
}
else
{
cin >> a;
cout << count[FindRoot(ufs,a)] << endl;
}
}
return 0;
}
3.字符串哈希
(1)题目示例
#include<iostream>
#include<vector>
#include<string>
using namespace std;
//使用常规的解法超出时间限制
typedef unsigned long long ULL;
const int P = 131; //经过计算,这个数的冲突概率低
ULL get(vector<ULL>& h,vector<ULL>& p,int l,int r)
{
return h[r] - h[l-1] * p[r-l+1];
}
int main()
{
int n,m;
cin >> n >> m;
string str;
cin >> str;
vector<ULL> h(n+1,0); //h[i] 表示前i个字符的哈希值
vector<ULL> p(n+1,0); //p[i] 表示p的i次方
p[0] = 1;
for(int i = 1; i <= n ;++i)
{
p[i] = p[i-1] * P;
h[i] = h[i-1] * P + str[i-1]; //求字符串的前缀和
}
int l1,r1,l2,r2;
while(m--)
{
cin >> l1 >> r1 >> l2 >> r2;
if(get(h,p,l1,r1) == get(h,p,l2,r2)) cout << "Yes" << endl;
else cout << "No" << endl;
}
return 0;
}