前几天被人面试,给了五道题,下面是问题及答案。
1 一堆数字去重
答案:
如果输入数组足够小的话,例如< 20,可以使用STL中的map来简单处理。如下:
for(int i = 0;i < array.size();i++)
{
map[array[i]] = 0;
}
map是平衡二叉树,在log2(array.size())的时间内即可实现。
如果输入数组非常大,超过了已有内存大小的话,可以分而治之,与词频统计同
一个道理,具体实现方法可以使用快排+合并。时间复杂度约为:
2*log2(array.size())
具体实现与CMU工具包原理相同。
2 如果上题中的数字很大,怎么办?
答案:
没想到居然和上一题相似。在此简单罗列CMU工具包的实现方法,因为以前看过。
(1)根据内存大小,分发足够多的内存。参考系统目录/proc/meminfo
(2)对原文件按照固定大小进行分割,分块处理,然后把处理结果写入临时目录,如/tmp。
2.1 对分割的每块文件,进行快排。
2.2 快排可以根据字符串指针进行,分割文件中的内容不移动。
2.3 把排序后的字符串指针写入临时文件。
2.4 根据同样方法继续处理其他块的文件。
(3)快排结束以后,对临时文件结果进行合并排序,可以两两合并,也可以多个合并,如
20个文件同时进行合并,在多个文件合并过程中,可以使用胜者树或败者树对比较结果
进行记录,以加快排序。
3 生成模型和区别模型的差异
答案:
1 概率计算上的差异:生成模型通过联合概率估计得到;区别模型通过条件概率计算得到。
2 生成模型有假设:根据某条件生成;区别模型没有此假设。
3 生成模型,例如HMM模型;区别模型,例如CRF。
4 生成模型得到的概率往往不交叉,而区别模型训练得到的概率往往相互作用和影响。
4 一堆很大的无序数字和一个有序的二叉排序树,如果判断二叉排序树中的数字都在前面的无序数字中出现,写出薇代码。
答案:
如果数组不够大的话,则首先对数组排序
1 qsort(大小为k的数组),得到数组A;
2 对二叉排序树进行遍历,得到排序数组B;
void Traverse(BTree *pTree,Array *A,int *n)
{
if(pTree == NULL)
return;
A[n] = pTree->data;
(*n)++;
Traverse(pTree->lift,A,&n);
Traverse(pTree->right,A,&n);
}
3 对两个有序数组进行对比即可。
for(n = 0;n < B.size();n++)
{
if(折半查找(B[n],A) != true)
{
printf("有节点不在数组A中/n");
return;
}
}
如果数组足够大而不能排序的话,则可以使用一个“笨”办法,
利用二叉查找树来查找数组,如果找到,则删掉二叉查找树中
的对应元素,这样二叉查找树的元素会逐渐减少,如果减少为
0,则说明m个元素都在数组中;如果查找结束还剩下树中节点,
则说明有些节点不在数组中。
5 判断句子的语义相似性,如:I hardly found the camera useful和the camera doesn't help me any,如果判断两个句子的相似性。
答案:
此题可以从理论上进行处理,也可以从工程上进行处理。
理论上的做法:
句法分析,得到所谓的格框架,如:施事、受事、与事等等,
通过词语间的格关系来进行计算。
但这种做法行不通,因为语义分析走不通。
工程上的做法:
1 以句子为单位作为一个统计窗口。
2 确定感兴趣的词语位置,如camera。
3 确定句子是否为肯定或否定。
如果句子的动词前出现not或no等等,则为否定;如果出现表示否定的副词,
如hardly、almost等等,也表示否定。这种否定副词是可以枚举的。
4 确定与camera相关的语义依赖关系。这种有语义倾向性的词语一般为动词、形
容词、副词。把这种词语全部作为倾向性候选。还可以依赖更多的资源,例如
wordnet、同义词词林等进行同义词扩展。
5 根据实例与实例之间的倾向性候选来计算语义距离,例如基于实例的倾向性候
选共现数目、基于实例的倾向性候选的权重来计算距离等等。