PTA 7-85 jmu-Java-05集合-4-倒排索引 题解
7-85 jmu-Java-05集合-4-倒排索引
解析
Tips: 题面迁移至末尾。
乍一看这题感觉很复杂,其实捋清楚逻辑关系就很清晰直白了。
形式化一下题面,就是给你一段以行为单位的文字,然后给你以一个“查找行”作为内容做一次查找,查找要求是找到包含“查找行”每一个单词的原文段的行,即建立倒排索引。
照着模拟就完了,题目复杂度不大,模拟不出问题即使很丑陋也能过的,写这篇主要是拾取一些淡忘的偏门方法:
1. 怎么实现读取一行后以每个单词进行处理?
java可以用以下方法实现分割:
String str = "apple,banana,orange";// 使用逗号作为分隔符来分割字符串
String[] fruits = str.split(",");
但 C++ 字符串处理貌似没有类似的方法,我们可以从字符串输入流入手:
string line, str;
getline(cin, line);
std::istringstream stream(line);
while(stream >> str);
用 istringstream treamname(string) 的方式,用一个字符串构建一个流对象,再从这个输入流对象中输入,就能实现用输入输出函数对字符串操作,十分优雅。
2.为什么字符串不能使用 %s 进行输入输出,这很反直觉啊?
简单来说,%s是 C 风格的输入输出方式,而字符串 std::string 是 C++ 标准库提供的一个类(对象)。
最直接的,对于C风格字符串,即 char 数组或 char * 指针指向的字符串,是以空字符'\0'来结尾的;而 std::string 对象不需要显示的定义结尾。
所以我们一般用 cin, cout 来处理 std::string 对象,%s 可以用于处理 char 数组整体输入输出。
如果硬要把 %s 套在字符串上,可以用 string.c_str() 函数获得一个 C 风格的字符串。
char ch[100];
scanf(" %s", &ch );
printf("%s\n", ch);
string str(ch); // std::string 构造器,传入ch数组为参数
printf("%s\n", str.c_str());
3. 怎么遍历一个map?
map 在存储键值对的时候,会按照字典序排列键值 key,这缘于 map 本质是基于红黑树。
于是我们可以用迭代器的方式遍历 map 的所有键值,就可以完成本题要求的按字典序输出所有单词出现的行的任务了。
map< string , vector<int> > mp;
for(auto it = mp.begin(); it != mp.end(); ++it){
printf("%s=[", it->first.c_str()); // it->first 显然指的是map的第一维即键值Key
for(int i=0; i < it->second.size(); i++){ // it->second 显然指的是map的第二维即Value,这里用vector可能是一个挺骚的操作
if(i && it->second[i] == it->second[i-1]) continue;
if(i) printf(", ");
printf("%d", it->second[i]);
}
printf("]\n");
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e3+10;
map< string , vector<int> > mp;
string inputs[maxn];
inline long long read(){
ll readtmp = 0, readflag = 1;
char readch = getchar();
while(readch < '0' || '9' < readch) {if(readch == '-') readflag =-1; readch= getchar();}
while('0' <= readch && readch <= '9') {readtmp = readtmp*10 + readch -'0'; readch = getchar();}
return readflag * readtmp;
}
int main(){
string line;
int row = 0;
while(getline(cin, line)){
if(line == "!!!!!") break;
row++; //记录行号
inputs[row] = line;
std::istringstream stream(line);
string str;
while(stream >> str){
mp[str].push_back(row);
}
}
for(auto it = mp.begin(); it != mp.end(); ++it){
printf("%s=[", it->first.c_str()); // Key
for(int i=0; i < it->second.size(); i++){ // Value
if(i && it->second[i] == it->second[i-1]) continue;
if(i) printf(", ");
printf("%d", it->second[i]);
}
printf("]\n");
}
while(getline(cin, line)){
//vector<int> count;
map<int, int> cnt;
std::istringstream stream(line);
string str;
int num = 0; // 统计词数
while(stream >> str){
num++;
//count+= mp[str]; // operator+= 需要C++17及以上标准?
//ount.insert(count.end(), mp[str].begin(), mp[str].end());
for(int i=0; i < mp[str].size(); i++) //对于一个单词,将它所有出现过的行加入统计映射cnt中
cnt[mp[str][i]] ++;
}
vector<int> print;
for(auto it = cnt.begin(); it != cnt.end(); ++it){
if(it->second < num) continue;
print.push_back(it->first); // 对于一行,被统计次数==单词数就认为它包含了所有需要查找的单词
}
if(print.size() < num) {printf("found 0 results\n"); continue;}
printf("[");
for(int i=0; i < print.size(); i++){
if(i!=0)printf(", ");
printf("%d", print[i]);
}
printf("]\n");
for(int i=0; i < print.size(); i++){
printf("line %d:%s\n", print[i], inputs[print[i]].c_str());
}
}
return 0;
}
题面
对若干行文字建立倒排索引(根据单词找到所在行号)。
然后根据关键字,在倒排索引查找进行查找,找到包含所有该关键字所在的行数并输出。
输入说明
若干行英文,以!!!!!为结束。
输入一行查询关键字,以1个空格为分隔
输出说明
输出所创建倒排索引。索引的key按照字母升序,索引的value按照行号升序
输出查询结果。如果找到,输出包含所查询关键字的行集(即,行集中每一行的内容);如果没找到输出found 0 results
输入样例
where are you from are you ok
this is a test
that is an apple
there are lots of apples you eat it
who are you
!!!!!
you are
eat
you test
abc
输出样例
a=[2]
an=[3]
apple=[3]
apples=[4]
are=[1, 4, 5]
eat=[4]
from=[1]
is=[2, 3]
it=[4]
lots=[4]
of=[4]
ok=[1]
test=[2]
that=[3]
there=[4]
this=[2]
where=[1]
who=[5]
you=[1, 4, 5]
[1, 4, 5]
line 1:where are you from are you ok
line 4:there are lots of apples you eat it
line 5:who are you
[4]
line 4:there are lots of apples you eat it
found 0 results
found 0 results

浙公网安备 33010602011771号