维护一个字符串集合,支持两种操作:
I x
向集合中插入一个字符串 \(x\);Q x
询问一个字符串在集合中出现了多少次。
- 所有输入的字符串总长度不超过 \(10^5\)( 也就是节点数)
const int N=100010;
int n;
char s[N];
int ch[N][26],cnt[N],idx;
void insert(char *s){
int p=0;
for(int i=0; s[i]; i ++){
int j=s[i]-'a';//字母映射
if(!ch[p][j])ch[p][j]=++idx;
p=ch[p][j];
}
cnt[p]++;//插入次数
}
int query(char *s){
int p=0;
for(int i=0; s[i]; i ++){
int j=s[i]-'a';
if(!ch[p][j]) return 0;
p=ch[p][j];
}
return cnt[p];
}
int main(){
scanf("%d",&n);
while(n--){
char op;
scanf("%s%s",&op,s);
if(op=='I')insert(s);
else printf("%d\n",query(s));
}
return 0;
}
应用举例:最大异或和,一组数中找两个数,期望他们异或起来最大。
- 分析:建01trie,对于每个数从高位开始每一位尽可能往相反方向走
- 时间复杂度:O(31n)
const int N=100010;
int n, a[N];
int ch[N*31][2], idx;
void insert(int x){
int p=0;
for(int i=30; i>=0; i--){
int j=x>>i&1; //取出第i位
if(!ch[p][j])ch[p][j]=++idx;
p=ch[p][j];
}
}
int query(int x){
int p=0,res=0;
for(int i=30; i>=0; i--){
int j=x>>i&1;
if(ch[p][!j]){
res += 1<<i; //累加位权
p=ch[p][!j];
}
else p=ch[p][j];
}
return res;
}
int main(){
cin>>n;
for(int i=1; i<=n; i++)
cin>>a[i],insert(a[i]);
int ans=0;
for(int i=1; i<=n; i++)
ans=max(ans,query(a[i]));
cout<<ans;
return 0;
}
最小异或对
如果求最小异或对,除了字典树外,有一个结论:n个数求最小异或对,排序后,一定是相邻的一对数。因为一个数和次小数,次次小数来异或,后者高位不同的多。
最小异或对必定是n个点排完序后,相邻两个点产生的异或值中的一个
给出证明
https://blog.csdn.net/WQhuanm/article/details/129804156