Luogu P3879 【[TJOI2010]阅读理解】
前言:
这个题一直有个疑问,最多一千行,每行五千字$1000\times5000=5e6$
$5e6\times26\times4\div1024\div1024\approx496Mb>125Mb$
尽管清楚实际空间需求不能到达$5e6$,如何计算直接对文章建$Trie$事实上所需的最大空间呢,本人对此并不清楚,也希望有大佬能为我解决一下这个问题(至于开$5e6\times26$通过的大佬,我想我这种凡人还达不到这个境界)
那么,就请我们权且认为直接对文章建$Trie$在空间上是不被允许的,至少是不够保险的
正文:
于是提供一种同样是基于$Trie$的方法——对询问建$Trie$
$10000\times20\times26\times4\div1024\div1024\approx20Mb$
占空间最多的$Trie$解决了,空间问题就不必担心了
下面我们考虑对询问建$Trie$下这个问题的解决
(这不是$Trie$的板子题吗,有什么可考虑的)
好吧,代码实现细节及一些注意事项,见代码了
#include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<algorithm> using namespace std; const int maxn=5e6,maxq=2e5+10; int cnt,a[maxq][26],n,m; vector<string>v[1010]; vector<int>ans[10010],val[maxq]; string tmp; int idx(char c) { return c-'a'; } void insert(int x) { cin>>tmp; int len=tmp.length(),now=0; for(int i=0;i<len;i++) { int c=idx(tmp[i]); now=a[now][c]?a[now][c]:a[now][c]=++cnt; } val[now].push_back(x); /*因为可能有相同的查询,这里的简单地用一个 整形标记就不太合适了,我选择了用vector记录 这个节点作为所有询问的标记*/ } void check(int x,int y) { int len=v[x][y].length(),now=0; for(int i=0;i<len;i++) { int c=idx(v[x][y][i]); if(!a[now][c]) return; //这个单词一定没有被查询过,直接退回 now=a[now][c]; } for(int i=0;i<val[now].size();i++) ans[val[now][i]].push_back(x); //同理在所有询问中保存这个单词曾经出现的文章 } int main() { scanf("%d",&n); for(int i=1,num;i<=n;i++) { scanf("%d",&num); for(int j=1;j<=num;j++) { cin>>tmp; v[i].push_back(tmp); //采用了vector来存储原文 } } scanf("%d",&m); for(int i=1;i<=m;i++) insert(i); for(int i=1;i<=n;i++) for(int j=0;j<v[i].size();j++) check(i,j); for(int i=1;i<=m;i++) { vector<int>::iterator pos=unique(ans[i].begin(),ans[i].end()); for(vector<int>::iterator it=ans[i].begin();it!=pos;it++) printf("%d ",*it); printf("\n"); } /*这里的输出,因为一篇文章里可能多次出现同一单词, 记得去重,一开始我用的unique,再输出vector中的 所有元素。但是unique去重时其实并没有删除这些元素 在这里也被坑了,所以改用迭代器*/ return 0; }