阅读理解
题目链接:ybt高效进阶2-4-4 / luogu P3879
题目大意
有一些句子,然后有一些问的单词。
对于每个问的单词,我们要求它在哪几个句子出现过。
思路
我们看到匹配单词,就会想到 Trie 树。
那用句子里的单词建 Trie 树不太好,因为单词太多,那我们可以用要用的单词建 Trie 树。
那我们建 Trie 树之后,就像模板一样搞就好了。
但是这题很狗,一个句子中可能同时出现一个你要的单词多次,或者有几个你要的单词的一样的。
那我们分别来解决这两个问题。
第一个问题,我们判断一下在这个句子中有没有出现过你现在发现的这个单词,没有才加入。
第二个问题,我们可以在建 Trie 树的时候如果原来有在这个点建过(或者说就是原来出现了这个字符串),那我们可以把它也记录下来(我是用邻接表的方式来记录)。
然后你找到一个数的时候,你就顺着邻接表,把每个数都标记一次。
当然你记录每个单词在哪里出现可以用 vector 来存。
代码
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
struct Trie {
int son[28], word;
}tree[200001];
int n, m, KK, size[10001], l, r, now, tmpn;
int have_next[10001];
char c[1001][5001], tmp[10001];
bool have[10001];
vector <int> a[10001];
void build(int word) {
now = 0;
for (int i = 0; i < tmpn; i++) {
if (!tree[now].son[tmp[i] - 'a']) tree[now].son[tmp[i] - 'a'] = ++KK;
now = tree[now].son[tmp[i] - 'a'];
}
if (!tree[now].word) tree[now].word = word;//因为规定的单词会有重复,那我们可以弄个类似邻接表的东西来标记完所有相同的位置
else {
have_next[word] = tree[now].word;
tree[now].word = word;
}
}
void ask(int op, int l, int r) {
now = 0;
for (int i = l; i <= r; i++)
if (!tree[now].son[c[op][i] - 'a']) return ;
else {
now = tree[now].son[c[op][i] - 'a'];
}
if (tree[now].word && !have[tree[now].word]) {//按着邻接表一个一个标记
have[tree[now].word] = 1;
a[tree[now].word].push_back(op);
int noww = tree[now].word;
while (have_next[noww]) {
noww = have_next[noww];
a[noww].push_back(op);
}
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &size[i]);
gets(c[i]);
}
scanf("%d", &m);
for (int i = 1; i <= m; i++) {//先把每一个要看的单词建Trie树
scanf("%s", &tmp);
tmpn = strlen(tmp);
build(i);
}
for (int i = 1; i <= n; i++) {
memset(have, 0, sizeof(have));
size[i] = strlen(c[i]);
size[i]--;
l = 1;
r = 1;
while (r <= size[i] - 1) {
while (c[i][r] != ' ' && r <= size[i] - 1) r++;
ask(i, l, r - 1);//看这个句子中的这个单词是否是要的单词
l = r + 1;
r = l;
}
}
for (int i = 1; i <= m; i++) {
KK = a[i].size();
for (int j = 0; j < KK; j++) {
printf("%d ", a[i][j]);
}
printf("\n");
}
return 0;
}