【TJOI 2010】阅读理解

Posted by WHZ0325 on 2021-07-13, Viewed times

题目描述

$N$ 篇短文,$M$ 个询问,每次询问其在哪几篇短文中出现过。

$1\le M\le 10^4$,$1\le N\le 10^3$,每篇短文不超过 $5\times 10^3$ 个字符,每个单词不超过 $20$ 个字符。

算法分析

用 Trie 树,对 $N$ 篇短文建立字典树的空间复杂度不对(内存限制 128 MB​),因此对询问串建立字典树。

易错点:

  • sscanf%n 取每次读取后的偏移量来持续向后读 fgets 得到的字符串。
  • scanf 读取 $N$ 后 fgets 需要读一个空行,由于评测系统下换行符可能不一样,用 getchar() 会出错。
  • 一个单词可能在一篇短文的多个单词中出现,需要判重。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <cstdio>
#include <cstring>
#include <vector>
#define N 1005
#define M 10005
#define SZ 200005
int n,m;std::vector<int> ans[M];
namespace Trie {
int ch[SZ][26],idx=1;std::vector<int> id[SZ];
inline void insert(char s[],int x) {
int len=strlen(s),rt=1;
for(int i=0;i<len;++i) {
int val=s[i]-'a';
if(!ch[rt][val]) ch[rt][val]=++idx;
rt=ch[rt][val];
}
id[rt].push_back(x);
}
inline void query(char s[],int x) {
int len=strlen(s),rt=1;bool error=false;
for(int i=0;i<len;++i) {
int val=s[i]-'a';
if(!ch[rt][val]) {error=true;break;}
rt=ch[rt][val];
}
if(!error) for(int i=0,sz=id[rt].size();i<sz;++i) {
int idx=id[rt][i];
if(ans[idx].empty()||ans[idx][ans[idx].size()-1]!=x) ans[id[rt][i]].push_back(x);
}
}
}
char ss[N][10005],s[25];
int main() {
scanf("%d",&n);fgets(ss[0],5005,stdin);
for(int i=0;i<n;++i) fgets(ss[i],5005,stdin);
scanf("%d",&m);
for(int i=0;i<m;++i) {
scanf("%s",s);
Trie::insert(s,i);
}
for(int i=0;i<n;++i) {
char *ptr=ss[i];int offset=0;
int l;sscanf(ptr,"%d%n",&l,&offset);ptr+=offset;
while(l--) {
sscanf(ptr,"%s%n",s,&offset);ptr+=offset;
Trie::query(s,i+1);
}
}
for(int i=0;i<m;++i) {
int sz=ans[i].size();bool first=true;
for(int j=0;j<sz;++j) {
if(first) first=false;
else putchar(' ');
printf("%d",ans[i][j]);
}
putchar('\n');
}
return 0;
}