Codeforces 163E (AC自动机&fail树+dfs序+树状数组)
http://codeforces.com/contest/163/problem/E
实在不好意思,自己画的时候确实有点粗心,感谢大家的提醒。。这次图应该画对了。。
题意:给你几个串,然后后面有些操作,分别是给你一个文本串问哪些模式串在文本串出现了多少次,剩下两个分别就是删除一些字符串,和恢复一些字符串。
做法:AC自动机,(据说还有后缀数组。。不会),如果做过阿狸的打字机这道题应该知道怎么往那方面做。
首先把所有的模式串建AC自动机,然后,建立fail树,就是fail指针反向,然后fail树跑dfs序,然后用树状数组维护。
如图bbaaa abaaab aa, 建 AC自动机,红色fail的指针,涂红和涂蓝的表示一个字符的结尾,黑色的表示结点编号
如图 fail的树,蓝色的表示AC自动机的结点编号,红色和绿色,表示dfs序,黑色的表示一个串的结尾。
在上面的图的基础上,树状的维护就简单了,其实代码就给看本事了,还是看代码吧,不懂的滴滴我;
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int tree[N];
void add(int x,int val)
{
for(;x<N;x+=x&-x) tree[x]+=val;
}
int query(int x)
{
int ret=0;
for(;x;x-=x&-x) ret+=tree[x];
return ret;
}
int nxt[N][26],fail[N],endd[N];
int tot=0,pos[N];
void Insert(char *s,int id)
{
int len=strlen(s);
int u=0;
for(int i=0;i<len;i++)
{
int x=s[i]-'a';
if(nxt[u][x]==0) nxt[u][x]=++tot;
u=nxt[u][x];
}
endd[u]++;
pos[id]=u;
}
void build()
{
queue<int>qu;
for(int i=0;i<26;i++)
if(nxt[0][i]!=0) qu.push(nxt[0][i]);
while(!qu.empty())
{
int u=qu.front();qu.pop();
endd[u]+=endd[fail[u]];
for(int i=0;i<26;i++)
{
if(nxt[u][i]!=0) fail[nxt[u][i]]=nxt[fail[u]][i],qu.push(nxt[u][i]);
else nxt[u][i]=nxt[fail[u]][i];
}
}
}
int n,k,in[N],ou[N],del[N],dfn=0;
vector<int>fait[N];
char str[N];
void dfs(int u)
{
in[u]=++dfn;
for(int i=0;i<fait[u].size();i++)
{
int v=fait[u][i]; dfs(v);
}
ou[u]=dfn;
}
int Mquery(char *s)
{
int len=strlen(s),u=0,ret=0;
for(int i=0;i<len;i++)
{
int x=s[i]-'a'; u=nxt[u][x];
if(endd[u])
ret+=query(in[u]);
}
return ret;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
memset(tree,0,sizeof(tree));
memset(nxt,0,sizeof(nxt));
memset(fail,0,sizeof(fail));
memset(endd,0,sizeof(endd));
memset(del,0,sizeof(del));
cin>>n>>k;
for(int i=1;i<=k;i++)
{
cin>>str;
Insert(str,i);
}
build();
for(int i=1;i<=tot;i++) fait[fail[i]].push_back(i);
dfs(0);
for(int i=1;i<=tot;i++)
{
add(in[i],endd[i]);
add(in[i]+1,-endd[i]);
}
char tt;int tmp;
for(int i=1;i<=n;i++)
{
cin>>tt;
if(tt=='?')
{
cin>>str;
cout<<Mquery(str)<<endl;
}
else if(tt=='-')
{
cin>>tmp;
if(!del[tmp])
{
del[tmp]=1;
add(in[pos[tmp]],-1);
add(ou[pos[tmp]]+1,1);
}
}
else
{
cin>>tmp;
if(del[tmp])
{
del[tmp]=0;
add(in[pos[tmp]],1);
add(ou[pos[tmp]]+1,-1);
}
}
}
return 0;
}