【BZOJ】2434: [Noi2011]阿狸的打字机 AC自动机+树状数组

传送门:【BZOJ】2434: [Noi2011]阿狸的打字机


题目分析:首先用字符串建AC自动机,然后用fail指针建fail指针树。

存在询问(x,y)则建边(y,x,i),其中i表示(x,y)是第i个询问。

将fail指针树dfs一次得到时间戳。

重新遍历字符串,遇到每一个字符就将该字符x对应树状数组的位置in[x]上+1,就在遇到第i个P时,则y为i的所有询问处理,存在询问(x,y,i)则ans[i] = sum ( ou[x] ) - sum ( in[x] - 1 )。遇到一个B就将当前字符x对应树状数组的位置in[x]上-1,然后跳到上一个结点。由于字符串长度最多n,所以插入以及删除的复杂度不会超过nlogn,同时查询的复杂度不会超过nlogn。总复杂度为O(nlogn)。

其思想还请自行体会。


代码如下:


#include <set>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

#pragma comment(linker, "/STACK:16777216")
#define rep( i , a , b ) for ( int i = ( a ) ; i <  ( b ) ; ++ i )
#define rev( i , a , b ) for ( int i = ( a ) ; i >= ( b ) ; -- i )
#define For( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i )
#define clr( a , x ) memset ( a , x , sizeof a )

const int MAXN = 100005 ;
const int MAXE = 200005 ;
const int INF = 0x3f3f3f3f ;

struct Edge {
	int v , n , idx ;
	Edge () {}
	Edge ( int v , int idx , int n ) : v ( v ) , idx ( idx ) , n ( n ) {}
} ;

struct ac_automaton {
	int next[MAXN][26] ;
	int word[MAXN] ;
	int fail[MAXN] ;
	int pre[MAXN] ;
	int root ;
	int cur ;
	int Q[MAXN] , head , tail ;

	char buf[MAXN] ;
	Edge E[MAXE] ;
	int H[MAXN] , query[MAXN] , cntE ;
	int in[MAXN] ;
	int ou[MAXN] ;
	int dfs_clock ;
	int ans[MAXN] ;
	int T[MAXN] ;
	int c[MAXN] ;
	int n ;

	int newnode () {
		rep ( i , 0 , 26 ) next[cur][i] = -1 ;
		return cur ++ ;
	}

	void clear () {
		n = 0 ;
		cur = 0 ;
		cntE = 0 ;
		dfs_clock = 0 ;
		clr ( T , 0 ) ;
		clr ( H , -1 ) ;
		clr ( query , -1 ) ;
		root = newnode () ;
	}

	void addedge ( int u , int v , int idx , int H[] ) {
		E[cntE] = Edge ( v , idx , H[u] ) ;
		H[u] = cntE ++ ;
	}

	void insert () {
		int now = root ;
		for ( int i = 0 ; buf[i] ; ++ i ) {
			if ( buf[i] == 'B' ) now = pre[now] ;
			else if ( buf[i] == 'P' ) {
				word[i] = ++ n ;
				c[n] = now ;
			} else {
				int idx = buf[i] - 'a' ;
				if ( next[now][idx] == -1 ) {
					next[now][idx] = newnode () ;
					pre[next[now][idx]] = now ;
				}
				now = next[now][idx] ;
			}
		}
	}

	void build () {
		head = tail = 0 ;
		fail[root] = root ;
		rep ( i , 0 , 26 ) {
			if ( ~next[root][i] ) {
				fail[next[root][i]] = root ;
				Q[tail ++] = next[root][i] ;
			} else next[root][i] = root ;
		}
		while ( head != tail ) {
			int now = Q[head ++] ;
			rep ( i , 0 , 26 ) {
				if ( ~next[now][i] ) {
					fail[next[now][i]] = next[fail[now]][i] ;
					Q[tail ++] = next[now][i] ;
				} else next[now][i] = next[fail[now]][i] ;
			}
		}
		rep ( i , 1 , cur ) addedge ( fail[i] , i , 0 , H ) ;//build tree
	}

	void dfs ( int u ) {
		in[u] = ++ dfs_clock ;
		for ( int i = H[u] ; ~i ; i = E[i].n ) dfs ( E[i].v ) ;
		ou[u] = dfs_clock ;
	}

	void add ( int x , int v ) {
		for ( int i = x ; i <= dfs_clock ; i += i & -i ) T[i] += v ;
	}

	int sum ( int x , int ans = 0 ) {
		for ( int i = x ; i ; i -= i & -i ) ans += T[i] ;
		return ans ;
	}

	void go () {
		int now = root ;
		for ( int i = 0 ; buf[i] ; ++ i ) {
			if ( buf[i] == 'B' ) {
				add ( in[now] , -1 ) ;
				now = pre[now] ;
			} else if ( buf[i] == 'P' ) {
				for ( int j = query[word[i]] ; ~j ; j = E[j].n ) {
					int v = E[j].v ;
					ans[E[j].idx] = sum ( ou[v] ) - sum ( in[v] - 1 ) ;
					//printf ( "idx = %d , v = %d %d %d\n" , E[j].idx , v , word[i] , ans[E[j].idx] ) ;
				}
			} else {
				int idx = buf[i] - 'a' ;
				now = next[now][idx] ;
				add ( in[now] , 1 ) ;
				//printf ( "now = %d\n" , now ) ;
			}
		}
	}
} ac ;

void solve () {
	int n , x , y ;
	ac.clear () ;
	ac.insert () ;
	ac.build () ;
	ac.dfs ( ac.root ) ;
	scanf ( "%d" , &n ) ;
	rep ( i , 0 , n ) {
		scanf ( "%d%d" , &x , &y ) ;
		ac.addedge ( y , ac.c[x] , i , ac.query ) ;
	}
	ac.go () ;
	rep ( i , 0 , n ) printf ( "%d\n" , ac.ans[i] ) ;
}

int main () {
	while ( ~scanf ( "%s" , ac.buf ) ) solve () ;
	return 0 ;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值