题目
http://codeforces.com/problemset/problem/163/E
AC自动机再构建失败树+树状数组统计/线段树
#include<stdio.h>
#include<string.h>
#define lowbit(i) (i&(-i))
const int MM = 1000005,M1 = 26;
int N,M;
char str[1000005];
int L;
int q[MM*2],front,last;
struct node{
int sign;
int fail;
int next[26];
}tree[MM];
int state[MM];
int head[MM];
struct NODE{
int v;
int next;
}edge[MM*3];
int cnt;
int dfn;
int st[MM*3];
int end[MM*3];
int pos[MM*3];
void init(int p)
{
for(int i=0;i<M1;i++)
tree[p].next[i]=0;
tree[p].sign=tree[p].fail=0;
}
int insert(char s[],int id)
{
int i=0,p=0,x;
while(s[i])
{
x=s[i]-'a';
if(tree[p].next[x]==0)
{
tree[p].next[x]=++L;
init(L);
}
p=tree[p].next[x];
i++;
}
tree[p].sign++;
return p;
}
void build( )
{
front = last = 0;
for(int i=0;i<M1;i++)
if(tree[0].next[i])
{
q[front++]=tree[0].next[i];
tree[tree[0].next[i]].fail=0;
}
while( last < front )
{
int t = q[last++];
for( int i = 0; i < M1; ++i )
{
int tt = tree[t].next[i];
int ff = tree[t].fail;
if( tree[t].next[i] )
{
tree[tt].fail = tree[ff].next[i];
q[front++] = tt;
}
else
tree[t].next[i] = tree[ff].next[i];
}
}
}
void add(int u,int v)
{
edge[cnt].v=v;
edge[cnt].next=head[u];
head[u]=cnt++;
}
void dfs(int u)
{
st[u]=dfn++;
for(int i=head[u];i!=-1;i=edge[i].next)
dfs(edge[i].v);
end[u]=dfn++;
}
int B[MM*2];
void update(int i,int v)
{
for(;i<dfn;i+=lowbit(i))
B[i]+=v;
}
int get_sum(int i)
{
int ret=0;
for(;i>0;i-=lowbit(i))
ret+=B[i];
return ret;
}
__int64 AC( char str[] )
{
int i = 1,cur = 0;
__int64 sum = 0;
while( str[i] )
{
int asc = str[i] - 'a';
cur = tree[cur].next[asc];
int p = cur;
sum+=get_sum(st[p]);
++i;
}
return sum;
}
int main( )
{
int i;
int num;
scanf( "%d%d",&N ,&M);
L=0;
init(0);
memset(state,0,sizeof(state));
for(i=1;i<=M;i++)
{
scanf( "%s",str );
state[i]=1;
pos[i]=insert(str,i);
}
build( );
cnt=0;
memset(head,-1,sizeof(head));
for(i=1;i<=L;i++) add(tree[i].fail,i);
dfn=1;//树状数组从下标1开始
dfs(0);
memset(B,0,sizeof(B));
for(i=1;i<=M;i++)
{
update(st[pos[i]],1);
update(end[pos[i]],-1);
}
for(i=1;i<=N;i++)
{
scanf("%s",str);
if(str[0]=='?')
{
printf( "%I64d\n",AC( str ) );
}
else
{
if(str[0]=='-')
{
sscanf(str, "-%d", &num);
if(state[num])
{
update(st[pos[num]],-1);update(end[pos[num]],1) ;
state[num]=0;
}
}
else
{
sscanf(str, "+%d", &num);
if(!state[num])
{
update(st[pos[num]],1);update(end[pos[num]],-1) ;
state[num]=1;
}
}
}
}
return 0;
}
(转)AC自动机构建失败树+树状数组/线段树统计
http://hi.baidu.com/pearfish16/blog/item/e04e020fde5bddd23bc7639e.html
虽然这道题我刚看到时就觉得它和AC自动机有关,但一直没想出来..网上这题解题报告也 很少,后来看了一份AC程序的某一段后,突然恍然大悟.... 这题最关键的就是,操作不是在AC自动机本身进行,而是在“Fail Tree”上进行的。 外每个点有且仅有一个失败指针,所以显然这是一棵树。 先考虑朴素的算法。构建出一个AC自动机,对于询问(x,y),只要将y单词的结束位置找出 来,将该位置到根上的所有点一一检验(虽然这样AC自动机没啥意义了...),沿着失败指 针走,走到根或x的结束位置为止。若走到x则答案加一。 除了构建AC自动机,输入的那串字符串还有一个重要的用途: 退栈一个元素便减去它,进栈便加上,遇到P就求和,即可完成。具体如下:将整棵树DFS前 序遍历,记录下每个点第一次访问时间,不难发现每一棵子树访问时间都是连续的。再记 下每个点最后一次访问时间,这样,第一次~最后一次之间的时间,访问的都是这棵子树。 有关AC自动机的练习,可以做Poj P2778,Poj P3691,Poj P1625。 |