说在前面
感觉这题还是挺经典的
所以还是写了记录一下…
题目
看题可进传送门
题目…略长,概括起来有点麻烦
解法
读完这道题之后,可以发现实际上它就是要我们求两个东西
「一个串被多少个串包含」和「一个串包含了多少串」
对于第一个问题:
一个串A被另一个串B包含,假设我们建出了A和B的AC自动机,那么说明从B的某个节点开始跳fail,最后可以跳到A的end。
如果只建A的AC自动机,也可以达到类似的效果。将B放在A的AC自动机上跑一遍,如果某一个被经过的节点,可以跳fail跳到A的end,那么就说明B包含了A。
于是只需要在fail树中查询:A的end的子树中是否有点被路过。即可知道B是否包含了A
那么现在是要统计「一个串被多少个串包含」,一个串只能算一次,于是可以这么干:先把所有经过的节点按照在fail树中的dfs序排序,然后这些点对应的位置打上+1标记,相邻点LCA的位置打上-1的标记。这样的话,一个串在一个子树中只会被统计一次。
对于第二个问题:
沿用AC自动机的思想,如果B包含了A,那么B的节点跳fail会跳到A。那么B一共包含了多少个串呢?就是「B所有节点的fail链的并」上有多少个end节点。统计方法和第一个问题类似,对于每个节点先求出该节点到root路径上有多少个end,把这个记为end_cnt 。答案就是:B的所有节点的end_cnt减去相邻点lca的end_cnt
实现的话,需要支持单点修改,区间查询,树状数组就好了
下面是自带大常数的代码
#include <map>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
vector<int> fir[20005] , sec[20005] ;
int N , M , ss[100005] , id_c , head[100005] , tp , global_ID ;
struct Node{
int end , id ;
Node *fail , *fa ;
map<int,Node*> ch ;
} *root , w[100005] , *tw = w ;
struct Path{
int pre , to ;
}p[200005] ;
struct BIT{
int b[100005] ;
void Add( int x ){ for( ; x <= id_c ; x += x&-x ) ++ b[x] ; }
void Del( int x ){ for( ; x <= id_c ; x += x&-x ) -- b[x] ; }
int Query( int x ){
int rt = 0 ;
for( ; x ; x -= x&-x ) rt += b[x] ;
return rt ;
} int Query( int L , int R ){ return Query( R ) - Query( L - 1 ) ; }
}B ;
void In( int t1 , int t2 ){
p[++tp] = ( Path ){ head[t1] , t2 } ; head[t1] = tp ;
}
void newNode( Node *&nd , Node *fa ){
nd = ++ tw ; nd->id = ++id_c ;
nd->ch.clear() ; nd->end = 0 ;
nd->fail = NULL ; nd->fa = fa ;
}
int ssID[100005] ;
void Insert( int *ss , int len , int ith ){
Node *nd = root ;
for( int i = 0 ; i < len ; i ++ ){
int id = ss[i] ;
if( !nd->ch.count( id ) ) newNode( nd->ch[id] , nd ) ;
nd = nd->ch[id] ;
} nd->end ++ ; ssID[ith] = nd->id ;
}
void buildAC(){
int fr = 1 , ba = 0 ;
Node *que[100005] ; que[++ba] = root ;
while( ba >= fr ){
Node *u = que[fr++] ;
for( map<int,Node*>::iterator it = u->ch.begin() ; it != u->ch.end() ; it ++ ){
Node *v = it->second , *p = u->fail ;
int id = it->first ;
while( p && !p->ch.count( id ) ) p = p->fail ;
v->fail = ( p ? p->ch[id] : root ) ;
In( v->fail->id , v->id ) ;
v->end += v->fail->end ;
que[++ba] = v ;
}
}
}
int dep[100005] , fa[17][100005] , logg , maxd , in[100005] , out[100005] , dfs_c ;
void dfsFailTree( int u ){
in[u] = ++dfs_c ;
for( int i = head[u] ; i ; i = p[i].pre ){
int v = p[i].to ;
fa[0][v] = u ; dep[v] = dep[u] + 1 ;
maxd = max( maxd , dep[v] ) ;
dfsFailTree( v ) ;
} out[u] = dfs_c ;
}
int LCA( int u , int v ){
bool print ;
print = ( u == 10 && v == 8 ) ;
if( dep[u] < dep[v] ) swap( u , v ) ;
int t = dep[u] - dep[v] , x = 0 ;
while( t ){
if( t&1 ) u = fa[x][u] ;
x ++ ; t >>= 1 ;
} if( u == v ) return u ;
for( int i = logg ; i >= 0 ; i -- )
if( fa[i][u] != fa[i][v] )
u = fa[i][u] , v = fa[i][v] ;
return fa[0][u] ;
}
void preWork(){
buildAC() ;
dfsFailTree( 1 ) ; fa[0][1] = 1 ;
for( logg = 1 ; 1 << ( logg + 1 ) <= maxd ; logg ++ ) ;
for( int i = 1 ; i <= logg ; i ++ )
for( int j = 1 ; j <= id_c ; j ++ )
fa[i][j] = fa[i-1][ fa[i-1][j] ] ;
}
int insta[100005] ;
int sta[100005] , topp ;
void Run( vector<int> &ss , int len ){
Node *nd = root ;
for( int i = 0 ; i < len ; i ++ ){
int id = ss[i] ;
while( nd && !nd->ch.count( id ) ) nd = nd->fail ;
if( nd ){
nd = nd->ch[id] ;
if( insta[ nd->id ] != global_ID ){
insta[ nd->id ] = global_ID ;
sta[++topp] = nd->id ;
}
} else nd = root ;
}
}
bool cmp( const int &A , const int &B ){ return in[A] < in[B] ; }
int nekoAns[100005] ;
void solve(){
for( int i = 1 ; i <= N ; i ++ ){
global_ID = i ; topp = 0 ;
Run( fir[i] , fir[i].size() ) ;
Run( sec[i] , sec[i].size() ) ;
sort( sta + 1 , sta + topp + 1 , cmp ) ;
B.Add( in[ sta[1] ] ) , nekoAns[i] += w[ sta[1] ].end ;
for( int j = 2 ; j <= topp ; j ++ ){
int Lca = LCA( sta[j-1] , sta[j] ) ;
B.Del( in[ Lca ] ) , nekoAns[i] -= w[Lca].end ;
B.Add( in[ sta[j] ] ) , nekoAns[i] += w[ sta[j] ].end ;
}
}
for( int i = 1 ; i <= M ; i ++ )
printf( "%d\n" , B.Query( in[ ssID[i] ] , out[ ssID[i] ] ) ) ;
for( int i = 1 ; i < N ; i ++ )
printf( "%d " , nekoAns[i] ) ;
printf( "%d" , nekoAns[N] ) ;
}
int main(){
newNode( root , NULL ) ;
scanf( "%d%d" , &N , &M ) ;
for( int i = 1 , len ; i <= N ; i ++ ){
scanf( "%d" , &len ) ; fir[i].resize( len ) ;
for( int j = 0 ; j < len ; j ++ ) scanf( "%d" , &fir[i][j] ) ;
scanf( "%d" , &len ) ; sec[i].resize( len ) ;
for( int j = 0 ; j < len ; j ++ ) scanf( "%d" , &sec[i][j] ) ;
} for( int i = 1 , len ; i <= M ; i ++ ){
scanf( "%d" , &len ) ;
for( int j = 0 ; j < len ; j ++ ) scanf( "%d" , &ss[j] ) ;
Insert( ss , len , i ) ;
} preWork() ; solve() ;
}