“搜索引擎(search engine)”这个名词可能对你来说并不陌生。一般来说,它能在互联网的有效(available)网页(pages)上,提取和组织出信息,并反馈(respond)与用户的问题(queries)关联性(relevant)最大的网页。像全球知名的搜索引擎GOOGLE,是我们上网时重要的工具。如下面这段会话,已成为我们日常生活的一部分: “像这个词语******是什么意思?” “额...我也不清楚,谷歌一下吧。” 在这道题中,你需要开发一个小搜索引擎。听起来不可思议,不是吗?不用担心,这里会有向导,来一步步教你如何高效地(efficiently)组织(organize)收集(collection)到的大量文本(texts),对一系列的问题作出快速的回应。你不用关心网页(web page)小程序,所有的网页都会以纯文本(text format)作为输入数据(input data)。而且,提供给你系统的问题集一定是合法(validate)的。 现代搜索引擎使用了一种叫“反向(inversion)”的技术来处理大量的文档。这个方法依赖于一种数据结构(data structure)的构造(construction)方式,叫做反向指针(index),就是标记每个项(term,或者说word)在哪些文档出现过。所有的项组成的集合称为词表(vocabulary),用V来表示(denoted as V)。简单的说,反向指针就是搜索的关键词ω,ω∈V。与其相关的数值是一个指针b(ω),指向一个作为中介(intermediate)的新增数据结构,叫“桶(bucket)”。桶本质(essentially)上就是一个指针列表,标记出所有出现过的文本集。桶的每个条目(entry)就是文档位置信息(document identifier,DID),包含了顺序编号(ordinal number)的某个文档及出现在第几行。 |
//{头文件模板 #include <algorithm> #include <bitset> #include <cassert> #include <cmath> #include <cstdio> #include <cstdlib> #include <cstring> #include <functional> #include <iomanip> #include <iostream> #include <list> #include <map> #include <queue> #include <set> #include <sstream> #include <stack> #include <string> #include <vector> using namespace std; //}头文件模板 #define rep(i,b) for(int i=0; i<(b); i++) #define foreach(i,a) for(__typeof((a).begin()) i=a.begin(); i!=(a).end(); ++i) #define FOR for (int j = limit[i]; j < limit[i + 1]; j++) typedef bool Bit[1505]; int n, lines, m; // n文档数, lines行数, m请求数 int limit[105]; // limit[i]: 第i篇文档从第几行开始 string doc[1505]; // 存储内容 map<string, Bit> Index; // Index[单词]: B标记了哪些行出现过 // 用s来更新Index void upDate(string s, int p) { string word; foreach(it, s) { if (isalpha(*it)) *it = tolower(*it); // 变小写 else *it = ' '; // 非字母变空白 } stringstream ss(s); while (ss >> word) Index[word][p] = true; } int main() { //{section: 文档数据输入 scanf("%d ", &n); rep(i, n) { limit[i] = lines; while (getline(cin, doc[lines]), doc[lines] != "**********") { upDate(doc[lines], lines); lines++; } } limit[n] = lines;//} //{section: 对获取的请求输出对应的内容 string s; // s存储每次得到的请求 Bit mark; //{记录哪些行应该输出 //}mark的解释: 因为每篇文档要用10个'-'隔开,比较麻烦,最后统一处理 bool *A, *B; scanf("%d ", &m); for (int iii = 0; iii < m; iii++) { getline(cin, s); //{subsection: 计算出mark中介 if (s[0] == 'N') { A = Index[s.substr(4)]; rep(i, n) { bool logo = true; FOR if (A[j]) { logo = false; break; } FOR mark[j] = logo; } } else if (s.find("AND") != string::npos) { int p = s.find(" AND "); A = Index[s.substr(0, p)]; B = Index[s.substr(p + 5)]; memset(mark, 0, sizeof(mark)); bool hasA, hasB; // 在同一文章中, 两个词是否都出现 rep(i ,n) { hasA = hasB = false; // 默认没出现 FOR if (A[j]) { hasA = true; break; } FOR if (B[j]) { hasB = true; break; } if (!(hasA&&hasB)) continue; FOR mark[j] = (A[j] || B[j]); } } else if (s.find("OR") != string::npos) { int p = s.find(" OR "); A = Index[s.substr(0, p)]; B = Index[s.substr(p + 4)]; rep(i, lines) mark[i] = (A[i] || B[i]); } else memcpy(mark, Index[s], sizeof(mark));//} //{subsection: 输出mark bool hasOut = false, needOut = false; // 记录上一轮是否有输出文档 rep(i, n) { if (hasOut) needOut = true; hasOut = false; FOR if (mark[j]) { if (needOut) { cout << "----------\n"; needOut = false; } cout << doc[j] << "\n"; hasOut = true; } } if (!(needOut||hasOut)) cout << "Sorry, I found nothing.\n"; cout << "==========\n";//} }//} return 0; }