题目大意:给定两个字符串集合S和T,初始给定S集合中的所有字符串,不断向T集合中添加字符串,以及询问S集合中的某个字符串在T集合中的多少个字符串中出现过
神题- -
首先对S集合的所有字符串构建fail树
T集合中每加入一个字符串,我们就将这个字符串在AC自动机上跑一遍,并记录经过的所有节点
根据fail树的性质,这些节点到根路径上的所有节点的并集的出现次数都加了1
因此我们要求的就是树链的并
求法如下:
将所有节点按照DFS序排序
每个点到根的路径上的所有节点权值+1
相邻两个点的LCA到根的路径上的所有节点权值-1
即是树链的并
当然我们不必修改路径查询单点 只需对单点进行修改 然后查询子树 用树状数组维护DFS序即可
这代码长度。。。。为了防止内存爆炸我用了线段树+RMQLCA。。。namespace套namespace泥萌怕不怕-。-
暴力艹标程是什么节奏0 0 快卡掉他们- -
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 2002002 using namespace std; int n,m,limit; char s[M]; namespace BIT{ int c[M]; void Update(int x,int y) { for(;x<=limit;x+=x&-x) c[x]+=y; } int Get_Ans(int x) { int re=0; for(;x;x-=x&-x) re+=c[x]; return re; } } namespace Fail_Tree{ struct abcd{ int to,next; }table[M]; int head[M],tot; int fa[M],dpt[M],pos[M],into[M],ed[M]; namespace ZKW_Segtree{ int Q,tree[8389000]; bool Compare(int x,int y) { if(!x) return false; if(!y) return true; return dpt[x]<dpt[y]; } void Build_Tree() { int i; for(i=Q-1;i;i--) tree[i]=min(tree[i<<1],tree[i<<1|1],Compare); } int Query(int x,int y) { int re=0; for(x+=Q-1,y+=Q+1;x^y^1;x>>=1,y>>=1) { if(~x&1) re=min(re,tree[x^1],Compare); if( y&1) re=min(re,tree[y^1],Compare); } return re; } } void DFS(int x,int array[]) { static int cnt1,cnt2; int i; dpt[x]=dpt[fa[x]]+1; pos[x]=++cnt1; array[into[x]=++cnt2]=x; for(i=head[x];i;i=table[i].next) { fa[table[i].to]=x; DFS(table[i].to,array); array[++cnt2]=x; } ed[x]=cnt1; } void Add(int x,int y) { table[++tot].to=y; table[tot].next=head[x]; head[x]=tot; } void Build_Tree() { using namespace ZKW_Segtree; for(Q=1;Q<=limit+limit;Q<<=1); DFS(1,tree+Q); ZKW_Segtree::Build_Tree(); } int LCA(int x,int y) { x=into[x];y=into[y]; if(x>y) swap(x,y); return ZKW_Segtree::Query(x,y); } } namespace Aho_Corasick_Automaton{ struct Trie{ Trie *son[26],*fail; }*root,mempool[M],*C=mempool; Trie *pos[100100]; void Insert(Trie *&p,char *s,int id) { if(!p) p=++C; if(!*s) { pos[id]=p; return ; } Insert(p->son[*s-'a'],s+1,id); } void Build_Tree() { static Trie *q[M]; int i,r=0,h=0; for(i=0;i<26;i++) { if(root->son[i]) (q[++r]=root->son[i])->fail=root; else root->son[i]=root; } while(r!=h) { Trie *p=q[++h]; for(i=0;i<26;i++) { if(p->son[i]) (q[++r]=p->son[i])->fail=p->fail->son[i]; else p->son[i]=p->fail->son[i]; } } for(i=2;i<=C-mempool;i++) Fail_Tree::Add(mempool[i].fail-mempool,i); limit=C-mempool; } int Get_Points(char *s,int a[]) { int i; Trie *p=root; for(i=1;s[i];i++) { p=p->son[s[i]-'a']; a[i]=p-mempool; } return a[i]=-1,i-1; } } bool Compare(int x,int y) { using namespace Fail_Tree; return pos[x]<pos[y]; } int main() { int i,j,p,x; cin>>n; for(i=1;i<=n;i++) { scanf("%s",s+1); Aho_Corasick_Automaton::Insert(Aho_Corasick_Automaton::root,s+1,i); } Aho_Corasick_Automaton::Build_Tree(); Fail_Tree::Build_Tree(); static int a[M],top; cin>>m; using namespace Fail_Tree; for(i=1;i<=m;i++) { scanf("%d",&p); if(p==1) { scanf("%s",s+1); x=Aho_Corasick_Automaton::Get_Points(s,a); sort(a+1,a+x+1,Compare); for(top=0,j=1;j<=x;j++) if(a[j]!=a[j+1]) a[++top]=a[j]; for(j=1;j<=top;j++) { x=a[j]; BIT::Update(pos[x],1); if(j>1) BIT::Update(pos[LCA(a[j-1],x)],-1); } } else { scanf("%d",&x); p=Aho_Corasick_Automaton::pos[x]-Aho_Corasick_Automaton::mempool; printf("%d\n",BIT::Get_Ans(ed[p])-BIT::Get_Ans(pos[p]-1) ); } } return 0; }