YZOJ P3897 Sevenk Love Oimaster
时间限制:1000MS 内存限制:131072KB
难度:\(7.5\)
-
题目描述
有 \(n\) 个大串和 \(q\) 个询问,每次给出一个字符串 \(s\) 询问在多少个大串中出现过。
-
输入格式
输入的第一行有两个整数分别代表 \(n\) 和 \(q\) 。
接下来的 \(n\) 行,分别给出题中所述的 n个只包含小写字母的字符串。
再接下来的 \(q\) 行,每行给出一个询问只包含小写字母的字符串。
-
输出格式
对于每一个询问,输出一行答案。
-
样例输入
1 2 3 4 5 6 7 |
3 3 abcabcabc aaa aafe abc a ca |
-
样例输出
1 2 3 |
1 3 1 |
-
数据规模与约定
\(n \leq 10000, q \leq 60000\) 。
原串总长度 \(\leq 100000\) 。
询问串总长度 \(\leq 360000\) 。
Source: BZOJ 2780 && SPOJ 8093
首先因为有多个模式串,所以建立一个广义SAM,并记录每个节点属于哪个串。
每个询问都在广义SAM上跑匹配,询问的答案就是最终匹配到的节点在 parent
树中的子树所包含的不同大串的数量。
显然可以在 parent
树上用
std::set 启发式合并,时间复杂度 \(O(nlog^2n)\) 。
或者按照 DFS序 离线用树状数组解决,时间复杂度 \(O(nlogn)\) 。
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 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
#include <cstdio> #include <cstdlib> #include <cstring> #include <climits> #include <set> inline void getstr(char s[],int&ls) { register char c=0; while(!(c>='a' && c<='z')) c=getchar(); ls=0; while(c>='a' && c<='z') s[++ls]=c,c=getchar(); s[ls+1]=0; return; } int gcnt,ghead[360505],gnext[750505],gnode[750505]; inline void insertLine(register int s,register int t) { gnext[++gcnt]=ghead[s],ghead[s]=gcnt,gnode[gcnt]=t; gnext[++gcnt]=ghead[t],ghead[t]=gcnt,gnode[gcnt]=s; } int cnt,parent[360505],mxval[360505],ch[360505][26]; inline int CreateNode(register int val=0) { parent[++cnt]=0,mxval[cnt]=val; for(register int j=0;j<26;j++) ch[cnt][j]=0; return cnt; } int root,last; inline int ExtendNode(register short c) { register int nw=CreateNode(mxval[last]+1),now=last; for(; now && !ch[now][c]; now=parent[now]) ch[now][c]=nw; if(!now) return parent[nw]=root,last=nw; register int q=ch[now][c]; if(mxval[q] == mxval[now]+1) return parent[nw]=q,last=nw; register int b=CreateNode(mxval[now]+1); parent[b]=parent[q],parent[nw]=parent[q]=b; for(register int j=0;j<26;j++) ch[b][j]=ch[q][j]; for(; now && ch[now][c]==q; now=parent[now]) ch[now][c]=b; return last=nw; } template<class T> inline void Merge(T&a,T&b) { if(a.size() < b.size()) a.swap(b); std::set<int>::iterator it=b.begin(); while(it != b.end()) a.insert(*it),it++; b.clear(); } int ans[360505]; std::set<int>apr[360001]; void DFS(register int o,register int fa) { for(register int j=ghead[o],t;j;j=gnext[j]) if((t=gnode[j]) != fa) { DFS(t,o); Merge(apr[o],apr[t]); } ans[o]=apr[o].size(); } char s[360505]; int main() { int N,Q;scanf("%d%d",&N,&Q); root=last=CreateNode(); for(register int lp=1;lp<=N;lp++) { int ls;getstr(s,ls); last=root; for(register int i=1;i<=ls;i++) { ExtendNode(s[i]-'a'); apr[last].insert(lp); } } for(register int i=2;i<=cnt;i++) insertLine(i,parent[i]); DFS(root,0); for(register int i=1;i<=Q;i++) { int ls;getstr(s,ls); register int now=root; for(register int i=1;i<=ls && now;i++) now=ch[now][s[i]-'a']; printf("%d\n",ans[now]); } return 0; } |