trie字典树

发布时间 2023-12-09 17:03:27作者: potential-star

维护一个字符串集合,支持两种操作:

  1. I x 向集合中插入一个字符串 \(x\)
  2. 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