题目大意:给定两个字符串集合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;
}