Trie树/字典树题目(2017今日头条笔试题:异或)

  1 /*
  2 本程序说明:
  3
  4 [编程题] 异或
  5 时间限制:1秒
  6 空间限制:32768K
  7 给定整数m以及n各数字A1,A2,..An,将数列A中所有元素两两异或,共能得到n(n-1)/2个结果,请求出这些结果中大于m的有多少个。
  8 输入描述:
  9 第一行包含两个整数n,m.
 10
 11 第二行给出n个整数A1,A2,...,An。
 12
 13 数据范围
 14
 15 对于30%的数据,1 <= n, m <= 1000
 16
 17 对于100%的数据,1 <= n, m, Ai <= 10^5
 18
 19
 20 输出描述:
 21 输出仅包括一行,即所求的答案
 22
 23 输入例子1:
 24 3 10
 25 6 5 10
 26
 27 输出例子1:
 28 2
 29
 30
 31 链接:https://www.nowcoder.com/questionTerminal/fc05f68c5f47438db54c6923ef23cf4a
 32 来源:牛客网
 33
 34 /*
 35 C++
 36 思路来源:牛客网“潇潇古月”,在此表示感谢。
 37 思路:
 38     直接计算肯定是超时的,所以这问题不能使用暴力破解,考虑到从高位到地位,依次进行位运算,
 39     如果两个数异或结果在某高位为1,而m的对应位为0,则肯定任何这两位异或结果为1的都会比m大。
 40     由此,考虑使用字典树(TrieTree)从高位到第位建立字典,再使用每个元素依次去字典中查对应
 41     高位异或为1, 而m为0的数的个数,相加在除以2既是最终的结果;直接贴出代码如下,非原创,欢迎讨论;
 42     补充:queryTrieTree在搜索的过程中,是从高位往低位搜索,那么,如果有一个数与字典中的数异或结果
 43     的第k位大于m的第k位,那么该数与对应分支中所有的数异或结果都会大于m, 否则,就要搜索在第k位异或
 44     相等的情况下,更低位的异或结果。queryTrieTree中四个分支的作用分别如下:
 45     1. aDigit=1, mDigit=1时,字典中第k位为0,异或结果为1,需要继续搜索更低位,第k位为1,异或结果为0,小于mDigit,不用理会;
 46     2. aDigit=0, mDigit=1时,字典中第k位为1,异或结果为1,需要继续搜索更低位,第k位为0,异或结果为0,小于mDigit,不用理会;
 47     3. aDigit=1, mDigit=0时,字典中第k位为0,异或结果为1,与对应分支所有数异或,结果都会大于m,第k位为1,异或结果为0,递归获得结果;
 48     4. aDigit=0, mDigit=0时,字典中第k位为1,异或结果为1,与对应分支所有数异或,结果都会大于m,第k位为0,异或结果为0,递归获得结果;
 49
 50 改进:
 51     1.字典树17位即可保证大于100000,移位范围为1~16位,则字典树构建时从16~0即可。
 52        字典树第一层不占位,实际上是15~-1层有数据,这也是数据中next的用法。
 53     2.queryTrieTree函数需要考虑到index为-1时的返回值。
 54
 55 时间复杂度:O(n);
 56 空间复杂度O(k),k为常数(trie树的高度),因此可以认为O(1)。
 57  */
 58 #include <iostream>
 59 #include <vector>
 60 using namespace std;
 61
 62 struct TrieTree
 63 {
 64     int count;//每个节点存的次数
 65     struct TrieTree* next[2]{NULL,NULL};//每个节点存储两个节点指针
 66     TrieTree():count(1){}
 67 };
 68
 69 TrieTree* buildTrieTree(const vector<int>& array)
 70 {
 71     TrieTree* trieTree = new TrieTree();
 72     for(int i=0;i<(int)array.size();++i)
 73     {
 74         TrieTree* cur = trieTree;
 75         for(int j=16;j>=0;--j)
 76         {
 77             int digit = (array[i] >> j) & 1;
 78             if(NULL == cur->next[digit])
 79                 cur->next[digit] = new TrieTree();
 80             else
 81                 ++(cur->next[digit]->count);
 82             cur = cur->next[digit];
 83         }
 84     }
 85     return trieTree;
 86 }
 87
 88 //查询字典树
 89 long long queryTrieTree(TrieTree*& trieTree, const int a, const int m, const int index)
 90 {
 91     if(NULL == trieTree)
 92         return 0;
 93
 94     TrieTree* cur = trieTree;
 95
 96     for(int i=index;i>=0;--i)
 97     {
 98         int aDigit = (a >> i) & 1;
 99         int mDigit = (m >> i) & 1;
100
101         if(1==aDigit && 1==mDigit)
102         {
103             if(NULL == cur->next[0])
104                 return 0;
105             cur = cur->next[0];
106         }
107         else if(0 == aDigit && 1==mDigit)
108         {
109             if(NULL == cur->next[1])
110                 return 0;
111             cur = cur->next[1];
112         }
113         else if(1 == aDigit && 0 == mDigit)
114         {
115             long long val0 =  (NULL == cur->next[0]) ? 0 : cur->next[0]->count;
116             long long val1 =  queryTrieTree(cur->next[1],a,m,i-1);
117             return val0+val1;
118         }
119         else if(0 == aDigit && 0 == mDigit)
120         {
121             long long val0 =  queryTrieTree(cur->next[0],a,m,i-1);
122             long long val1 =  (NULL == cur->next[1]) ? 0 : cur->next[1]->count;
123             return val0+val1;
124         }
125     }
126     return 0;//此时index==-1,这种情况肯定返回0(其他情况在循环体中都考虑到了)
127 }
128
129 //结果可能超过了int范围,因此用long long
130 long long solve(const vector<int>& array, const int& m)
131 {
132     TrieTree* trieTree = buildTrieTree(array);
133     long long result = 0;
134     for(int i=0;i<(int)array.size();++i)
135     {
136         result += queryTrieTree(trieTree,array[i],m,16);
137     }
138     return result /2;
139 }
140
141 int main()
142 {
143     int n,m;
144     while(cin>>n>>m)
145     {
146         vector<int> array(n);
147         for(int i=0;i<n;++i)
148             cin>>array[i];
149         cout<< solve(array,m) <<endl;
150     }
151     return 0;
152 }

关于trie数的其他应用,可参见http://www.cnblogs.com/dlutxm/archive/2011/10/26/2225660.html,感觉写的不错。

时间: 2024-12-22 01:29:46

Trie树/字典树题目(2017今日头条笔试题:异或)的相关文章

2018春招-今日头条笔试题-第三题(python)

题目描述:2018春招-今日头条笔试题5题(后附大佬答案-c++版) 解题思路: 本题的做法最重要的应该是如何拼出'1234567890',对于输入表达试获得对应的结果利用python内置函数eval()即可以实现.利用5个字符串来表达'1234567890',如下(为了好看,里面加了tab空格符) '66666 ....6 66666 66666 6...6 66666 66666 66666 66666 66666''6...6 ....6 ....6 ....6 6...6 6.... 6

数组元素前移问题(今日头条笔试题)

问题描述:给定一个整数数组和一个整数x,将数组中元素值为x的元素都放到数组的前面,其他元素的相对顺次不变. 例如:原数组为{4,0,1,0,2,,3,6,0,5},x=0,则调整后的数组为{0,0,0,4,1,2,3,6,5} 分析:该问题存在多种解法. 解法一:辅助数组法.创建一个和原数组一样长度的数组,从原数组的尾部开始扫描,如果元素值是x,则忽略. 否则,将元素拷贝到新数组的末尾.扫描完原数组后,再在新数组未重新赋值的元素全部赋值为x. 最后再将新数组的所有元素赋值到原数组中. 假设原数组

今日头条笔试题 1~n的每个数,按字典序排完序后,第m个数是什么?

# 由于n和m都是10^18的范围,暴力明显不行,只能dfs了.# 先预处理n的十进制,存到num数组中,长度计算出来为len, 答案存到Num中.# 写个函数cntOfBeginNum(int *Num,int anslen),计算以num为前缀的小于等于n的数的个数cnt,考虑长度比n的十进制长度小的数,则cnt+=1+10+100+1000......# 长度和n相等的则要特判一下,前缀Num和num的前缀有3种关系,> = < ,先判断是哪种,再计算.# 然后考虑答案的最高位非0,我们

今日头条笔试题--2018(3) 【 优先队列 】

时间限制:1秒 空间限制:81920K 产品经理(PM)有很多好的idea,而这些idea需要程序员实现.现在有N个PM,在某个时间会想出一个 idea,每个 idea 有提出时间.所需时间和优先等级.对于一个PM来说,最想实现的idea首先考虑优先等级高的,相同的情况下优先所需时间最小的,还相同的情况下选择最早想出的,没有 PM 会在同一时刻提出两个 idea. 同时有M个程序员,每个程序员空闲的时候就会查看每个PM尚未执行并且最想完成的一个idea,然后从中挑选出所需时间最小的一个idea独

剑指Offer——Trie树(字典树)

剑指Offer--Trie树(字典树) Trie树 Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种.典型应用是统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计.它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高. Trie的核心思想是空间换时间.利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的. Trie树也有它的缺点,Trie树的内存消耗非常大.当然,或许用左儿子右兄弟的方法建树的话,可能会好点.可见,优

【数据结构】前缀树/字典树/Trie

[前缀树] 用来保存一个映射(通常情况下 key 为字符串  value 为字符串所代表的信息) 例如:一个单词集合 words = {  apple, cat,  water  }   其中 key 为单词      value 代表该单词是否存在 words[ 'apple' ] = 存在     而     word[ ' abc' ] = 不存在 图示:一个保存了8个键的trie结构,"A", "to", "tea", "ted

Hash树(散列树)和Trie树(字典树、前缀树)

1.Hash树 理想的情况是希望不经过任何比较,一次存取便能得到所查的记录, 那就必须在记的存储位置和它的关键字之间建立一个确定的对应关系f,使每个关键字和一个唯一的存储位置相对应.因而在查找时,只要根据这个对应关系f找到 给定值K的像f(K).由此,不需要进行比较便可直接取得所查记录.在此,我们称这个对应关系为哈希(Hash)函数,按这个思想建立的表为哈希表. 在哈希表中对于不同的关键字可能得到同一哈希地址,这种现象称做冲突.在一般情况下,冲突只能尽可能地减少,而不能完全避免.因为哈希函数是从

【学习总结】数据结构-Trie/前缀树/字典树-及其最常见的操作

Trie/前缀树/字典树 Trie (发音为 "try") 或前缀树是一种树数据结构,用于检索字符串数据集中的键. 一种树形结构,是一种哈希树的变种. 典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计. 优点:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高. 应用: 自动补全 END 原文地址:https://www.cnblogs.com/anliux/p/12590368.html

9-11-Trie树/字典树/前缀树-查找-第9章-《数据结构》课本源码-严蔚敏吴伟民版

课本源码部分 第9章  查找 - Trie树/字典树/前缀树(键树) ——<数据结构>-严蔚敏.吴伟民版        源码使用说明  链接??? <数据结构-C语言版>(严蔚敏,吴伟民版)课本源码+习题集解析使用说明        课本源码合辑  链接??? <数据结构>课本源码合辑        习题集全解析  链接??? <数据结构题集>习题解析合辑        本源码引入的文件  链接? Status.h.Scanf.c        相关测试数据