概念
它是一种用于搜索文档的方法。一般场景用于给出一个关键字,返回这个关键字相应的文章的集合。
案列
假设我们有如下几篇文档
- D1 = “谷歌地图之父跳槽Facebook”
- D2 = “谷歌地图之父加盟Facebook”
- D3 =“谷歌地图创始人拉斯离开谷歌加盟Facebook”
- D4 = “谷歌地图创始人跳槽Facebook与Wave项目取消有关”
- D5 = “谷歌地图创始人拉斯加盟社交网站Facebook”
对每篇文档都进行分词以后,可以这些文档中包含的关键词有:{谷歌,地图,之父,跳槽,Facebook,加盟,创始人,拉斯,离开,与,Wave,项目,取消,有关,社交,网站}
首先,去掉“与”这样的没有实际表意作用的停用词。 然后,对每一个词建立一个链表,表中的每个元素都是包含该词的某篇文档的标识。 于是,得到如下的倒排链集合:
- 谷歌—{D1,D2,D3,D4,D5}
- 地图—{D1,D2,D3,D4,D5}
- Facebook—{D1,D2,D3,D4,D5}
- 创始人—{D3,D4,D5}
- 加盟—{D2,D3,D5}
- 拉斯—{D3,D5}
- 之父—{D1,D2}
- 跳槽—{D1,D2}
- 离开—{D3}
- 取消—{D4}
- 项目—{D4}
- 有关—{D4}
- 社交—{D5}
- 网站—{D5}
实现思想
我们主要使用unordered_map / map 来实现,所以查询文档的关键字集合时间复杂度在O(1)和O(lg^2N)。
我们可以使用 大概这样的结构 map < string,List < int > > inverted index;
通过map 快速查找对应关键词,然后对应关键词上有相关联的文章集合
代码实现
#include <iostream>
#include <map>
#include <list>
#include <vector>
#include <string>
#include <set>
using namespace std;
class InvIndex : public map<string, list<int>>
{
public:
vector<vector<string>> docs; //文档正排表
public:
//向索引中加入一个文档
void add(vector<string>& doc)
{
//在正排表里记录该文档
docs.push_back(doc);
int curDocID = docs.size(); //现在代码:使得文档编号从1开始 原始代码:int curDocID = docs.size()-1;
//遍历doc里所有的term
for (int w = 0; w < doc.size(); w++)
{
map<string,list<int>>::iterator it;
it = this->find(doc[w]);
//如果该term的倒排链不存在,新建倒排链
if (it == this->end())
{
(*this)[doc[w]] ;
it = this->find(doc[w]);
}
//在倒排链末尾插入新的文档
it->second.push_back(curDocID);
}
}
//在索引中进行一次查询
void retrieve(vector<string>& query, set<int>& docIDs)
{
int termNum = query.size();
//合并所有term的倒排链
docIDs.clear();
for (int t = 0; t < termNum; t++)
{
map<string,list<int>>::iterator it;
//该term倒排链不存在则跳过
if ((it = this->find(query[t])) != this->end())
docIDs.insert(it->second.begin(),it->second.end());
}
}
};
int main()
{
string D1_tmp[] = {"谷歌","地图","之父","跳槽","Facebook"};
int D1_tmp_size = sizeof(D1_tmp)/sizeof(string);
vector<string> D1(D1_tmp,D1_tmp+D1_tmp_size);
string D2_tmp[] = {"谷歌","地图","之父","加盟","Facebook"};
int D2_tmp_size = sizeof(D2_tmp)/sizeof(string);
vector<string> D2(D2_tmp,D2_tmp+D2_tmp_size);
string D3_tmp[] = {"谷歌","地图","创始人","拉斯","离开","谷歌","加盟","Facebook"};
int D3_tmp_size = sizeof(D3_tmp)/sizeof(string);
vector<string> D3(D3_tmp,D3_tmp+D3_tmp_size);
string D4_tmp[] = {"谷歌","地图","创始人","跳槽","Facebook","与","Wave","项目","取消","有关"};
int D4_tmp_size = sizeof(D4_tmp)/sizeof(string);
vector<string> D4(D4_tmp,D4_tmp+D4_tmp_size);
string D5_tmp[] = {"谷歌","地图","创始人","拉斯","加盟","社交","网站","Facebook"};
int D5_tmp_size = sizeof(D5_tmp)/sizeof(string);
vector<string> D5(D5_tmp,D5_tmp+D5_tmp_size);
InvIndex * inverted_index = new InvIndex<string>;
inverted_index->add(D1);
inverted_index->add(D2);
inverted_index->add(D3);
inverted_index->add(D4);
inverted_index->add(D5);
string str_query[] = {"谷歌","地图","之父","跳槽","Facebook","创始人","加盟","拉斯","离开","与","Wave","项目","取消","有关","社交","网站"};
for(int i = 0; i < sizeof(str_query)/sizeof(string); i++)
{
vector<string> query;
query.push_back(str_query[i]);
cout << str_query[i] << " ";
set<int> docSet;
inverted_index->retrieve(query,docSet);
set<int>::iterator it;
for (it = docSet.begin(); it != docSet.end(); it++)
{
cout << "D" << *it << " ";
}
cout << endl;
}
return 0;
}