传送门:【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 ;
}